diff options
author | Cy Schubert <cy@FreeBSD.org> | 2017-10-18 03:44:27 +0000 |
---|---|---|
committer | Cy Schubert <cy@FreeBSD.org> | 2017-10-18 03:44:27 +0000 |
commit | 256810032c472505440606bd9c1c4f7dbf06b0a2 (patch) | |
tree | d019f4f1ae29b86cbd3e9e74f832689976a267cf /src | |
parent | b834757ea3bcd1bba3381ff7cab216458d8f7efb (diff) |
Notes
Diffstat (limited to 'src')
304 files changed, 20753 insertions, 5385 deletions
diff --git a/src/ap/accounting.c b/src/ap/accounting.c index a096de4d3e514..0aacc3c95b084 100644 --- a/src/ap/accounting.c +++ b/src/ap/accounting.c @@ -1,6 +1,6 @@ /* * hostapd / RADIUS Accounting - * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2009, 2012-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -41,6 +41,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, size_t len; int i; struct wpabuf *b; + struct os_time now; msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, radius_client_get_id(hapd->radius)); @@ -49,44 +50,24 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, return NULL; } - if (sta) { - radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); - - if ((hapd->conf->wpa & 2) && - !hapd->conf->disable_pmksa_caching && - sta->eapol_sm && sta->eapol_sm->acct_multi_session_id_hi) { - os_snprintf(buf, sizeof(buf), "%08X+%08X", - sta->eapol_sm->acct_multi_session_id_hi, - sta->eapol_sm->acct_multi_session_id_lo); - if (!radius_msg_add_attr( - msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, - (u8 *) buf, os_strlen(buf))) { - wpa_printf(MSG_INFO, - "Could not add Acct-Multi-Session-Id"); - goto fail; - } - } - } else { - radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd)); - } - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE, status_type)) { wpa_printf(MSG_INFO, "Could not add Acct-Status-Type"); goto fail; } - if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, - RADIUS_ATTR_ACCT_AUTHENTIC) && - !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, - hapd->conf->ieee802_1x ? - RADIUS_ACCT_AUTHENTIC_RADIUS : - RADIUS_ACCT_AUTHENTIC_LOCAL)) { - wpa_printf(MSG_INFO, "Could not add Acct-Authentic"); - goto fail; - } - if (sta) { + if (!hostapd_config_get_radius_attr( + hapd->conf->radius_acct_req_attr, + RADIUS_ATTR_ACCT_AUTHENTIC) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, + hapd->conf->ieee802_1x ? + RADIUS_ACCT_AUTHENTIC_RADIUS : + RADIUS_ACCT_AUTHENTIC_LOCAL)) { + wpa_printf(MSG_INFO, "Could not add Acct-Authentic"); + goto fail; + } + /* Use 802.1X identity if available */ val = ieee802_1x_get_identity(sta->eapol_sm, &len); @@ -147,6 +128,32 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, wpa_printf(MSG_ERROR, "Could not add CUI from ACL"); goto fail; } + + if (sta->ipaddr && + !radius_msg_add_attr_int32(msg, + RADIUS_ATTR_FRAMED_IP_ADDRESS, + be_to_host32(sta->ipaddr))) { + wpa_printf(MSG_ERROR, + "Could not add Framed-IP-Address"); + goto fail; + } + } + + os_get_time(&now); + if (now.sec > 1000000000 && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, + now.sec)) { + wpa_printf(MSG_INFO, "Could not add Event-Timestamp"); + goto fail; + } + + /* + * Add Acct-Delay-Time with zero value for the first transmission. This + * will be updated within radius_client.c when retransmitting the frame. + */ + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_DELAY_TIME, 0)) { + wpa_printf(MSG_INFO, "Could not add Acct-Delay-Time"); + goto fail; } return msg; @@ -164,19 +171,25 @@ static int accounting_sta_update_stats(struct hostapd_data *hapd, if (hostapd_drv_read_sta_data(hapd, data, sta->addr)) return -1; - if (sta->last_rx_bytes > data->rx_bytes) - sta->acct_input_gigawords++; - if (sta->last_tx_bytes > data->tx_bytes) - sta->acct_output_gigawords++; - sta->last_rx_bytes = data->rx_bytes; - sta->last_tx_bytes = data->tx_bytes; + if (!data->bytes_64bit) { + /* Extend 32-bit counters from the driver to 64-bit counters */ + if (sta->last_rx_bytes_lo > data->rx_bytes) + sta->last_rx_bytes_hi++; + sta->last_rx_bytes_lo = data->rx_bytes; + + if (sta->last_tx_bytes_lo > data->tx_bytes) + sta->last_tx_bytes_hi++; + sta->last_tx_bytes_lo = data->tx_bytes; + } hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: " - "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u " - "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u", - sta->last_rx_bytes, sta->acct_input_gigawords, - sta->last_tx_bytes, sta->acct_output_gigawords); + HOSTAPD_LEVEL_DEBUG, + "updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d", + data->rx_bytes, sta->last_rx_bytes_hi, + sta->last_rx_bytes_lo, + data->tx_bytes, sta->last_tx_bytes_hi, + sta->last_tx_bytes_lo, + data->bytes_64bit); return 0; } @@ -217,12 +230,14 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "starting accounting session %08X-%08X", - sta->acct_session_id_hi, sta->acct_session_id_lo); + "starting accounting session %016llX", + (unsigned long long) sta->acct_session_id); os_get_reltime(&sta->acct_session_start); - sta->last_rx_bytes = sta->last_tx_bytes = 0; - sta->acct_input_gigawords = sta->acct_output_gigawords = 0; + sta->last_rx_bytes_hi = 0; + sta->last_rx_bytes_lo = 0; + sta->last_tx_bytes_hi = 0; + sta->last_tx_bytes_lo = 0; hostapd_drv_sta_clear_stats(hapd, sta->addr); if (!hapd->conf->radius->acct_server) @@ -251,8 +266,7 @@ static void accounting_sta_report(struct hostapd_data *hapd, int cause = sta->acct_terminate_cause; struct hostap_sta_driver_data data; struct os_reltime now_r, diff; - struct os_time now; - u32 gigawords; + u64 bytes; if (!hapd->conf->radius->acct_server) return; @@ -266,7 +280,6 @@ static void accounting_sta_report(struct hostapd_data *hapd, } os_get_reltime(&now_r); - os_get_time(&now); os_reltime_sub(&now_r, &sta->acct_session_start, &diff); if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, diff.sec)) { @@ -287,48 +300,42 @@ static void accounting_sta_report(struct hostapd_data *hapd, wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets"); goto fail; } + if (data.bytes_64bit) + bytes = data.rx_bytes; + else + bytes = ((u64) sta->last_rx_bytes_hi << 32) | + sta->last_rx_bytes_lo; if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_OCTETS, - data.rx_bytes)) { + (u32) bytes)) { wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets"); goto fail; } - gigawords = sta->acct_input_gigawords; -#if __WORDSIZE == 64 - gigawords += data.rx_bytes >> 32; -#endif - if (gigawords && - !radius_msg_add_attr_int32( - msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, - gigawords)) { + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, + (u32) (bytes >> 32))) { wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords"); goto fail; } + if (data.bytes_64bit) + bytes = data.tx_bytes; + else + bytes = ((u64) sta->last_tx_bytes_hi << 32) | + sta->last_tx_bytes_lo; if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_OUTPUT_OCTETS, - data.tx_bytes)) { + (u32) bytes)) { wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets"); goto fail; } - gigawords = sta->acct_output_gigawords; -#if __WORDSIZE == 64 - gigawords += data.tx_bytes >> 32; -#endif - if (gigawords && - !radius_msg_add_attr_int32( - msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, - gigawords)) { + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, + (u32) (bytes >> 32))) { wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords"); goto fail; } } - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, - now.sec)) { - wpa_printf(MSG_INFO, "Could not add Event-Timestamp"); - goto fail; - } - if (eloop_terminated()) cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT; @@ -375,22 +382,17 @@ void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) eloop_cancel_timeout(accounting_interim_update, hapd, sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "stopped accounting session %08X-%08X", - sta->acct_session_id_hi, - sta->acct_session_id_lo); + "stopped accounting session %016llX", + (unsigned long long) sta->acct_session_id); sta->acct_session_started = 0; } } -void accounting_sta_get_id(struct hostapd_data *hapd, - struct sta_info *sta) +int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta) { - sta->acct_session_id_lo = hapd->acct_session_id_lo++; - if (hapd->acct_session_id_lo == 0) { - hapd->acct_session_id_hi++; - } - sta->acct_session_id_hi = hapd->acct_session_id_hi; + return radius_gen_session_id((u8 *) &sta->acct_session_id, + sizeof(sta->acct_session_id)); } @@ -437,12 +439,14 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) if (!msg) return; - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, - RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT)) - { - wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); - radius_msg_free(msg); - return; + if (hapd->acct_session_id) { + char buf[20]; + + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) hapd->acct_session_id); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + (u8 *) buf, os_strlen(buf))) + wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id"); } if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0) @@ -450,6 +454,63 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) } +static void accounting_interim_error_cb(const u8 *addr, void *ctx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + unsigned int i, wait_time; + int res; + + sta = ap_get_sta(hapd, addr); + if (!sta) + return; + sta->acct_interim_errors++; + if (sta->acct_interim_errors > 10 /* RADIUS_CLIENT_MAX_RETRIES */) { + wpa_printf(MSG_DEBUG, + "Interim RADIUS accounting update failed for " MACSTR + " - too many errors, abandon this interim accounting update", + MAC2STR(addr)); + sta->acct_interim_errors = 0; + /* Next update will be tried after normal update interval */ + return; + } + + /* + * Use a shorter update interval as an improved retransmission mechanism + * for failed interim accounting updates. This allows the statistics to + * be updated for each retransmission. + * + * RADIUS client code has already waited RADIUS_CLIENT_FIRST_WAIT. + * Schedule the first retry attempt immediately and every following one + * with exponential backoff. + */ + if (sta->acct_interim_errors == 1) { + wait_time = 0; + } else { + wait_time = 3; /* RADIUS_CLIENT_FIRST_WAIT */ + for (i = 1; i < sta->acct_interim_errors; i++) + wait_time *= 2; + } + res = eloop_deplete_timeout(wait_time, 0, accounting_interim_update, + hapd, sta); + if (res == 1) + wpa_printf(MSG_DEBUG, + "Interim RADIUS accounting update failed for " MACSTR + " (error count: %u) - schedule next update in %u seconds", + MAC2STR(addr), sta->acct_interim_errors, wait_time); + else if (res == 0) + wpa_printf(MSG_DEBUG, + "Interim RADIUS accounting update failed for " MACSTR + " (error count: %u)", MAC2STR(addr), + sta->acct_interim_errors); + else + wpa_printf(MSG_DEBUG, + "Interim RADIUS accounting update failed for " MACSTR + " (error count: %u) - no timer found", MAC2STR(addr), + sta->acct_interim_errors); +} + + /** * accounting_init: Initialize accounting * @hapd: hostapd BSS data @@ -457,20 +518,15 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) */ int accounting_init(struct hostapd_data *hapd) { - struct os_time now; - - /* Acct-Session-Id should be unique over reboots. Using a random number - * is preferred. If that is not available, take the current time. Mix - * in microseconds to make this more likely to be unique. */ - os_get_time(&now); - if (os_get_random((u8 *) &hapd->acct_session_id_hi, - sizeof(hapd->acct_session_id_hi)) < 0) - hapd->acct_session_id_hi = now.sec; - hapd->acct_session_id_hi ^= now.usec; + if (radius_gen_session_id((u8 *) &hapd->acct_session_id, + sizeof(hapd->acct_session_id)) < 0) + return -1; if (radius_client_register(hapd->radius, RADIUS_ACCT, accounting_receive, hapd)) return -1; + radius_client_set_interim_error_cb(hapd->radius, + accounting_interim_error_cb, hapd); accounting_report_state(hapd, 1); diff --git a/src/ap/accounting.h b/src/ap/accounting.h index dcc54ee94b549..de5a33f3c7ce5 100644 --- a/src/ap/accounting.h +++ b/src/ap/accounting.h @@ -10,9 +10,10 @@ #define ACCOUNTING_H #ifdef CONFIG_NO_ACCOUNTING -static inline void accounting_sta_get_id(struct hostapd_data *hapd, - struct sta_info *sta) +static inline int accounting_sta_get_id(struct hostapd_data *hapd, + struct sta_info *sta) { + return 0; } static inline void accounting_sta_start(struct hostapd_data *hapd, @@ -34,7 +35,7 @@ static inline void accounting_deinit(struct hostapd_data *hapd) { } #else /* CONFIG_NO_ACCOUNTING */ -void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta); +int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta); void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta); void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta); int accounting_init(struct hostapd_data *hapd); diff --git a/src/ap/acs.c b/src/ap/acs.c index 03d797fe8836a..5e8380535854f 100644 --- a/src/ap/acs.c +++ b/src/ap/acs.c @@ -599,8 +599,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface) wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz", n_chans == 1 ? 20 : n_chans == 2 ? 40 : - n_chans == 4 ? 80 : - -1); + 80); for (i = 0; i < iface->current_mode->num_channels; i++) { double total_weight; @@ -933,6 +932,9 @@ enum hostapd_chan_status acs_init(struct hostapd_iface *iface) return HOSTAPD_CHAN_ACS; } + if (!iface->current_mode) + return HOSTAPD_CHAN_INVALID; + acs_cleanup(iface); err = acs_request_scan(iface); diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 9a96e50b7385b..228de2baf9462 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -38,6 +38,8 @@ static void hostapd_config_free_vlan(struct hostapd_bss_config *bss) void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) { + dl_list_init(&bss->anqp_elem); + bss->logger_syslog_level = HOSTAPD_LEVEL_INFO; bss->logger_stdout_level = HOSTAPD_LEVEL_INFO; bss->logger_syslog = (unsigned int) -1; @@ -63,6 +65,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) bss->dtim_period = 2; bss->radius_server_auth_port = 1812; + bss->eap_sim_db_timeout = 1; bss->ap_max_inactivity = AP_MAX_INACTIVITY; bss->eapol_version = EAPOL_VERSION; @@ -180,6 +183,7 @@ struct hostapd_config * hostapd_config_defaults(void) conf->ignore_assoc_probability = 0.0; conf->ignore_reassoc_probability = 0.0; conf->corrupt_gtk_rekey_mic_probability = 0.0; + conf->ecsa_ie_only = 0; #endif /* CONFIG_TESTING_OPTIONS */ conf->acs = 0; @@ -198,13 +202,6 @@ int hostapd_mac_comp(const void *a, const void *b) } -int hostapd_mac_comp_empty(const void *a) -{ - macaddr empty = { 0 }; - return os_memcmp(a, empty, sizeof(macaddr)); -} - - static int hostapd_config_read_wpa_psk(const char *fname, struct hostapd_ssid *ssid) { @@ -410,6 +407,19 @@ void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **l) } +static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf) +{ + struct anqp_element *elem; + + while ((elem = dl_list_first(&conf->anqp_elem, struct anqp_element, + list))) { + dl_list_del(&elem->list); + wpabuf_free(elem->payload); + os_free(elem); + } +} + + void hostapd_config_free_bss(struct hostapd_bss_config *conf) { struct hostapd_eap_user *user, *prev_user; @@ -454,6 +464,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->private_key); os_free(conf->private_key_passwd); 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->pac_opaque_encr_key); @@ -523,6 +534,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->network_auth_type); os_free(conf->anqp_3gpp_cell_net); os_free(conf->domain_name); + hostapd_config_free_anqp_elem(conf); #ifdef CONFIG_RADIUS_TEST os_free(conf->dump_msk_file); @@ -555,6 +567,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) #endif /* CONFIG_HS20 */ wpabuf_free(conf->vendor_elements); + wpabuf_free(conf->assocresp_elements); os_free(conf->sae_groups); @@ -594,6 +607,8 @@ void hostapd_config_free(struct hostapd_config *conf) #ifdef CONFIG_ACS os_free(conf->acs_chan_bias); #endif /* CONFIG_ACS */ + wpabuf_free(conf->lci); + wpabuf_free(conf->civic); os_free(conf); } @@ -610,7 +625,7 @@ void hostapd_config_free(struct hostapd_config *conf) * Perform a binary search for given MAC address from a pre-sorted list. */ int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, - const u8 *addr, int *vlan_id) + const u8 *addr, struct vlan_description *vlan_id) { int start, end, middle, res; @@ -650,11 +665,26 @@ int hostapd_rate_found(int *list, int rate) } -int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id) +int hostapd_vlan_valid(struct hostapd_vlan *vlan, + struct vlan_description *vlan_desc) { struct hostapd_vlan *v = vlan; + int i; + + if (!vlan_desc->notempty || vlan_desc->untagged < 0 || + vlan_desc->untagged > MAX_VLAN_ID) + return 0; + for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) { + if (vlan_desc->tagged[i] < 0 || + vlan_desc->tagged[i] > MAX_VLAN_ID) + return 0; + } + if (!vlan_desc->untagged && !vlan_desc->tagged[0]) + return 0; + while (v) { - if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD) + if (!vlan_compare(&v->vlan_desc, vlan_desc) || + v->vlan_id == VLAN_ID_WILDCARD) return 1; v = v->next; } @@ -756,7 +786,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, return -1; } - if (full_config && hostapd_mac_comp_empty(bss->bssid) != 0) { + if (full_config && !is_zero_ether_addr(bss->bssid)) { size_t i; for (i = 0; i < conf->num_bss; i++) { @@ -811,6 +841,15 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, } #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + if (full_config && conf->ieee80211ac && + bss->ssid.security_policy == SECURITY_STATIC_WEP) { + bss->disable_11ac = 1; + wpa_printf(MSG_ERROR, + "VHT (IEEE 802.11ac) with WEP is not allowed, disabling VHT capabilities"); + } +#endif /* CONFIG_IEEE80211AC */ + #ifdef CONFIG_WPS if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) { wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid " @@ -847,6 +886,15 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, } #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + if (full_config && bss->mbo_enabled && (bss->wpa & 2) && + bss->ieee80211w == NO_MGMT_FRAME_PROTECTION) { + wpa_printf(MSG_ERROR, + "MBO: PMF needs to be enabled whenever using WPA2 with MBO"); + return -1; + } +#endif /* CONFIG_MBO */ + return 0; } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index de470a969b500..8c8f7e286bdae 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -10,12 +10,14 @@ #define HOSTAPD_CONFIG_H #include "common/defs.h" +#include "utils/list.h" #include "ip_addr.h" #include "common/wpa_common.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "wps/wps.h" #include "fst/fst.h" +#include "vlan.h" /** * mesh_conf - local MBSS state and settings @@ -39,6 +41,10 @@ struct mesh_conf { #define MESH_CONF_SEC_AUTH BIT(1) #define MESH_CONF_SEC_AMPE BIT(2) unsigned int security; + enum mfp_options ieee80211w; + unsigned int pairwise_cipher; + unsigned int group_cipher; + unsigned int mgmt_group_cipher; int dot11MeshMaxRetries; int dot11MeshRetryTimeout; /* msec */ int dot11MeshConfirmTimeout; /* msec */ @@ -52,7 +58,7 @@ typedef u8 macaddr[ETH_ALEN]; struct mac_acl_entry { macaddr addr; - int vlan_id; + struct vlan_description vlan_id; }; struct hostapd_radius_servers; @@ -102,6 +108,7 @@ struct hostapd_ssid { #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1 #define DYNAMIC_VLAN_NAMING_END 2 int vlan_naming; + int per_sta_vif; #ifdef CONFIG_FULL_DYNAMIC_VLAN char *vlan_tagged_interface; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ @@ -113,6 +120,7 @@ struct hostapd_ssid { struct hostapd_vlan { struct hostapd_vlan *next; int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ + struct vlan_description vlan_desc; char ifname[IFNAMSIZ + 1]; int configured; int dynamic_vlan; @@ -124,9 +132,14 @@ struct hostapd_vlan { }; #define PMK_LEN 32 +#define MIN_PASSPHRASE_LEN 8 +#define MAX_PASSPHRASE_LEN 63 struct hostapd_sta_wpa_psk_short { struct hostapd_sta_wpa_psk_short *next; + unsigned int is_passphrase:1; u8 psk[PMK_LEN]; + char passphrase[MAX_PASSPHRASE_LEN + 1]; + int ref; /* (number of references held) - 1 */ }; struct hostapd_wpa_psk { @@ -205,6 +218,13 @@ struct hostapd_nai_realm_data { } eap_method[MAX_NAI_EAP_METHODS]; }; +struct anqp_element { + struct dl_list list; + u16 infoid; + struct wpabuf *payload; +}; + + /** * struct hostapd_bss_config - Per-BSS configuration */ @@ -231,6 +251,7 @@ struct hostapd_bss_config { struct hostapd_eap_user *eap_user; char *eap_user_sqlite; char *eap_sim_db; + unsigned int eap_sim_db_timeout; int eap_server_erp; /* Whether ERP is enabled on internal EAP server */ struct hostapd_ip_addr own_ip_addr; char *nas_identifier; @@ -242,6 +263,7 @@ struct hostapd_bss_config { int radius_das_port; unsigned int radius_das_time_window; int radius_das_require_event_timestamp; + int radius_das_require_message_authenticator; struct hostapd_ip_addr radius_das_client_addr; u8 *radius_das_shared_secret; size_t radius_das_shared_secret_len; @@ -332,6 +354,7 @@ struct hostapd_bss_config { int check_crl; unsigned int tls_session_lifetime; char *ocsp_stapling_response; + char *ocsp_stapling_response_multi; char *dh_file; char *openssl_ciphers; u8 *pac_opaque_encr_key; @@ -358,6 +381,7 @@ struct hostapd_bss_config { int ap_max_inactivity; int ignore_broadcast_ssid; + int no_probe_resp_if_max_sta; int wmm_enabled; int wmm_uapsd; @@ -481,8 +505,11 @@ struct hostapd_bss_config { unsigned int nai_realm_count; struct hostapd_nai_realm_data *nai_realm_data; + struct dl_list anqp_elem; /* list of struct anqp_element */ + u16 gas_comeback_delay; int gas_frag_limit; + int gas_address3; u8 qos_map_set[16 + 2 * 21]; unsigned int qos_map_set_len; @@ -536,6 +563,7 @@ struct hostapd_bss_config { #endif /* CONFIG_RADIUS_TEST */ struct wpabuf *vendor_elements; + struct wpabuf *assocresp_elements; unsigned int sae_anti_clogging_threshold; int *sae_groups; @@ -551,12 +579,22 @@ struct hostapd_bss_config { #define MESH_ENABLED BIT(0) int mesh; - int radio_measurements; + u8 radio_measurements[RRM_CAPABILITIES_IE_LEN]; int vendor_vht; + int use_sta_nsts; char *no_probe_resp_if_seen_on; char *no_auth_if_seen_on; + + int pbss; + +#ifdef CONFIG_MBO + int mbo_enabled; +#endif /* CONFIG_MBO */ + + int ftm_responder; + int ftm_initiator; }; @@ -638,6 +676,9 @@ struct hostapd_config { u8 vht_oper_centr_freq_seg0_idx; u8 vht_oper_centr_freq_seg1_idx; + /* Use driver-generated interface addresses when adding multiple BSSs */ + u8 use_driver_iface_addr; + #ifdef CONFIG_FST struct fst_iface_cfg fst_cfg; #endif /* CONFIG_FST */ @@ -652,6 +693,7 @@ struct hostapd_config { double ignore_assoc_probability; double ignore_reassoc_probability; double corrupt_gtk_rekey_mic_probability; + int ecsa_ie_only; #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_ACS @@ -662,11 +704,13 @@ struct hostapd_config { } *acs_chan_bias; unsigned int num_acs_chan_bias; #endif /* CONFIG_ACS */ + + struct wpabuf *lci; + struct wpabuf *civic; }; int hostapd_mac_comp(const void *a, const void *b); -int hostapd_mac_comp_empty(const void *a); struct hostapd_config * hostapd_config_defaults(void); void hostapd_config_defaults_bss(struct hostapd_bss_config *bss); void hostapd_config_free_eap_user(struct hostapd_eap_user *user); @@ -674,13 +718,14 @@ void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p); void hostapd_config_free_bss(struct hostapd_bss_config *conf); void hostapd_config_free(struct hostapd_config *conf); int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, - const u8 *addr, int *vlan_id); + const u8 *addr, struct vlan_description *vlan_id); 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); int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); -int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id); +int hostapd_vlan_valid(struct hostapd_vlan *vlan, + struct vlan_description *vlan_desc); const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id); struct hostapd_radius_attr * diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index 6cafcb7493511..f1394654d3a8e 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -33,10 +33,36 @@ u32 hostapd_sta_flags_to_drv(u32 flags) res |= WPA_STA_SHORT_PREAMBLE; if (flags & WLAN_STA_MFP) res |= WPA_STA_MFP; + if (flags & WLAN_STA_AUTH) + res |= WPA_STA_AUTHENTICATED; + if (flags & WLAN_STA_ASSOC) + res |= WPA_STA_ASSOCIATED; return res; } +static int add_buf(struct wpabuf **dst, const struct wpabuf *src) +{ + if (!src) + return 0; + if (wpabuf_resize(dst, wpabuf_len(src)) != 0) + return -1; + wpabuf_put_buf(*dst, src); + return 0; +} + + +static int add_buf_data(struct wpabuf **dst, const u8 *data, size_t len) +{ + if (!data || !len) + return 0; + if (wpabuf_resize(dst, len) != 0) + return -1; + wpabuf_put_data(*dst, data, len); + return 0; +} + + int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf **beacon_ret, struct wpabuf **proberesp_ret, @@ -49,82 +75,38 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, pos = buf; pos = hostapd_eid_time_adv(hapd, pos); - if (pos != buf) { - if (wpabuf_resize(&beacon, pos - buf) != 0) - goto fail; - wpabuf_put_data(beacon, buf, pos - buf); - } + if (add_buf_data(&beacon, buf, pos - buf) < 0) + goto fail; pos = hostapd_eid_time_zone(hapd, pos); - if (pos != buf) { - if (wpabuf_resize(&proberesp, pos - buf) != 0) - goto fail; - wpabuf_put_data(proberesp, buf, pos - buf); - } + if (add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; pos = buf; pos = hostapd_eid_ext_capab(hapd, pos); - if (pos != buf) { - if (wpabuf_resize(&assocresp, pos - buf) != 0) - goto fail; - wpabuf_put_data(assocresp, buf, pos - buf); - } + if (add_buf_data(&assocresp, buf, pos - buf) < 0) + goto fail; pos = hostapd_eid_interworking(hapd, pos); pos = hostapd_eid_adv_proto(hapd, pos); pos = hostapd_eid_roaming_consortium(hapd, pos); - if (pos != buf) { - if (wpabuf_resize(&beacon, pos - buf) != 0) - goto fail; - wpabuf_put_data(beacon, buf, pos - buf); - - if (wpabuf_resize(&proberesp, pos - buf) != 0) - goto fail; - wpabuf_put_data(proberesp, buf, pos - buf); - } + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; #ifdef CONFIG_FST - if (hapd->iface->fst_ies) { - size_t add = wpabuf_len(hapd->iface->fst_ies); - - if (wpabuf_resize(&beacon, add) < 0) - goto fail; - wpabuf_put_buf(beacon, hapd->iface->fst_ies); - if (wpabuf_resize(&proberesp, add) < 0) - goto fail; - wpabuf_put_buf(proberesp, hapd->iface->fst_ies); - if (wpabuf_resize(&assocresp, add) < 0) - goto fail; - wpabuf_put_buf(assocresp, hapd->iface->fst_ies); - } + if (add_buf(&beacon, hapd->iface->fst_ies) < 0 || + add_buf(&proberesp, hapd->iface->fst_ies) < 0 || + add_buf(&assocresp, hapd->iface->fst_ies) < 0) + goto fail; #endif /* CONFIG_FST */ - if (hapd->wps_beacon_ie) { - if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) < - 0) - goto fail; - wpabuf_put_buf(beacon, hapd->wps_beacon_ie); - } - - if (hapd->wps_probe_resp_ie) { - if (wpabuf_resize(&proberesp, - wpabuf_len(hapd->wps_probe_resp_ie)) < 0) - goto fail; - wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie); - } + if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 || + add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0) + goto fail; #ifdef CONFIG_P2P - if (hapd->p2p_beacon_ie) { - if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) < - 0) - goto fail; - wpabuf_put_buf(beacon, hapd->p2p_beacon_ie); - } - - if (hapd->p2p_probe_resp_ie) { - if (wpabuf_resize(&proberesp, - wpabuf_len(hapd->p2p_probe_resp_ie)) < 0) - goto fail; - wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie); - } + if (add_buf(&beacon, hapd->p2p_beacon_ie) < 0 || + add_buf(&proberesp, hapd->p2p_probe_resp_ie) < 0) + goto fail; #endif /* CONFIG_P2P */ #ifdef CONFIG_P2P_MANAGER @@ -148,8 +130,7 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, #ifdef CONFIG_WPS if (hapd->conf->wps_state) { struct wpabuf *a = wps_build_assoc_resp_ie(); - if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) - wpabuf_put_buf(assocresp, a); + add_buf(&assocresp, a); wpabuf_free(a); } #endif /* CONFIG_WPS */ @@ -169,44 +150,36 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, if (hapd->p2p_group) { struct wpabuf *a; a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS); - if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) - wpabuf_put_buf(assocresp, a); + add_buf(&assocresp, a); wpabuf_free(a); } #endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_HS20 - pos = buf; - pos = hostapd_eid_hs20_indication(hapd, pos); - if (pos != buf) { - if (wpabuf_resize(&beacon, pos - buf) != 0) - goto fail; - wpabuf_put_data(beacon, buf, pos - buf); - - if (wpabuf_resize(&proberesp, pos - buf) != 0) - goto fail; - wpabuf_put_data(proberesp, buf, pos - buf); - } + pos = hostapd_eid_hs20_indication(hapd, buf); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; pos = hostapd_eid_osen(hapd, buf); - if (pos != buf) { - if (wpabuf_resize(&beacon, pos - buf) != 0) - goto fail; - wpabuf_put_data(beacon, buf, pos - buf); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; +#endif /* CONFIG_HS20 */ - if (wpabuf_resize(&proberesp, pos - buf) != 0) +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled) { + pos = hostapd_eid_mbo(hapd, buf, sizeof(buf)); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0 || + add_buf_data(&assocresp, buf, pos - buf) < 0) goto fail; - wpabuf_put_data(proberesp, buf, pos - buf); } -#endif /* CONFIG_HS20 */ +#endif /* CONFIG_MBO */ - if (hapd->conf->vendor_elements) { - size_t add = wpabuf_len(hapd->conf->vendor_elements); - if (wpabuf_resize(&beacon, add) == 0) - wpabuf_put_buf(beacon, hapd->conf->vendor_elements); - if (wpabuf_resize(&proberesp, add) == 0) - wpabuf_put_buf(proberesp, hapd->conf->vendor_elements); - } + add_buf(&beacon, hapd->conf->vendor_elements); + add_buf(&proberesp, hapd->conf->vendor_elements); + add_buf(&assocresp, hapd->conf->assocresp_elements); *beacon_ret = beacon; *proberesp_ret = proberesp; @@ -390,7 +363,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, - u32 flags, u8 qosinfo, u8 vht_opmode) + u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, + int set) { struct hostapd_sta_add_params params; @@ -412,6 +386,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, params.vht_opmode = vht_opmode; params.flags = hostapd_sta_flags_to_drv(flags); params.qosinfo = qosinfo; + params.support_p2p_ps = supp_p2p_ps; + params.set = set; return hapd->driver->sta_add(hapd->drv_priv, ¶ms); } @@ -468,7 +444,7 @@ int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type, return -1; return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr, bss_ctx, drv_priv, force_ifname, if_addr, - bridge, use_existing); + bridge, use_existing, 1); } @@ -647,16 +623,28 @@ int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd, int hostapd_drv_send_mlme(struct hostapd_data *hapd, const void *msg, size_t len, int noack) { + if (!hapd->driver || !hapd->driver->send_mlme || !hapd->drv_priv) + return 0; + return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0, + NULL, 0); +} + + +int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd, + const void *msg, size_t len, int noack, + const u16 *csa_offs, size_t csa_offs_len) +{ if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) return 0; - return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0); + return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0, + csa_offs, csa_offs_len); } int hostapd_drv_sta_deauth(struct hostapd_data *hapd, const u8 *addr, int reason) { - if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) + if (!hapd->driver || !hapd->driver->sta_deauth || !hapd->drv_priv) return 0; return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr, reason); @@ -666,7 +654,7 @@ int hostapd_drv_sta_deauth(struct hostapd_data *hapd, int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, const u8 *addr, int reason) { - if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) + if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv) return 0; return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr, reason); @@ -687,6 +675,36 @@ int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *data, size_t len) { + const u8 *bssid; + const u8 wildcard_bssid[ETH_ALEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv) + return 0; + bssid = hapd->own_addr; + if (!is_multicast_ether_addr(dst) && + len > 0 && data[0] == WLAN_ACTION_PUBLIC) { + struct sta_info *sta; + + /* + * Public Action frames to a STA that is not a member of the BSS + * shall use wildcard BSSID value. + */ + sta = ap_get_sta(hapd, dst); + if (!sta || !(sta->flags & WLAN_STA_ASSOC)) + bssid = wildcard_bssid; + } + return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst, + hapd->own_addr, bssid, data, len, 0); +} + + +int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd, + unsigned int freq, + unsigned int wait, const u8 *dst, + const u8 *data, size_t len) +{ if (hapd->driver == NULL || hapd->driver->send_action == NULL) return 0; return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst, @@ -736,7 +754,7 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface, int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, u8 qos_map_set_len) { - if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL) + if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv) return 0; return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set, qos_map_set_len); @@ -762,6 +780,20 @@ static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd, } +void hostapd_get_ext_capa(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + + if (!hapd->driver || !hapd->driver->get_ext_capab) + return; + + hapd->driver->get_ext_capab(hapd->drv_priv, WPA_IF_AP_BSS, + &iface->extended_capa, + &iface->extended_capa_mask, + &iface->extended_capa_len); +} + + int hostapd_drv_do_acs(struct hostapd_data *hapd) { struct drv_acs_params params; diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 82eaf3f08bb5b..0bb7954ec061f 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -41,7 +41,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, u16 listen_interval, const struct ieee80211_ht_capabilities *ht_capab, const struct ieee80211_vht_capabilities *vht_capab, - u32 flags, u8 qosinfo, u8 vht_opmode); + u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps, + int set); int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, size_t elem_len); @@ -88,6 +89,9 @@ int hostapd_drv_set_key(const char *ifname, const u8 *key, size_t key_len); int hostapd_drv_send_mlme(struct hostapd_data *hapd, const void *msg, size_t len, int noack); +int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd, + const void *msg, size_t len, int noack, + const u16 *csa_offs, size_t csa_offs_len); int hostapd_drv_sta_deauth(struct hostapd_data *hapd, const u8 *addr, int reason); int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, @@ -95,6 +99,10 @@ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *data, size_t len); +int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd, + unsigned int freq, + unsigned int wait, const u8 *dst, + const u8 *data, size_t len); int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, u16 auth_alg); int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, @@ -120,6 +128,8 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd, int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, u8 qos_map_set_len); +void hostapd_get_ext_capa(struct hostapd_iface *iface); + static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd, int enabled) { @@ -150,7 +160,7 @@ static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd, static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd, const u8 *addr) { - if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) + if (!hapd->driver || !hapd->driver->sta_remove || !hapd->drv_priv) return 0; return hapd->driver->sta_remove(hapd->drv_priv, addr); } @@ -273,7 +283,7 @@ static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd, static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf, size_t buflen) { - if (hapd->driver == NULL || hapd->driver->status == NULL) + if (!hapd->driver || !hapd->driver->status || !hapd->drv_priv) return -1; return hapd->driver->status(hapd->drv_priv, buf, buflen); } @@ -332,7 +342,7 @@ static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd, static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd) { - if (hapd->driver == NULL || hapd->driver->stop_ap == NULL) + if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv) return 0; return hapd->driver->stop_ap(hapd->drv_priv); } diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c index 13604edc49406..e7308a01d7432 100644 --- a/src/ap/ap_mlme.c +++ b/src/ap/ap_mlme.c @@ -59,6 +59,7 @@ void mlme_authenticate_indication(struct hostapd_data *hapd, MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg)); if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP)) mlme_deletekeys_request(hapd, sta); + ap_sta_clear_disconnect_timeouts(hapd, sta); } @@ -106,6 +107,7 @@ void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta) MAC2STR(sta->addr)); if (sta->auth_alg != WLAN_AUTH_FT) mlme_deletekeys_request(hapd, sta); + ap_sta_clear_disconnect_timeouts(hapd, sta); } @@ -130,6 +132,7 @@ void mlme_reassociate_indication(struct hostapd_data *hapd, MAC2STR(sta->addr)); if (sta->auth_alg != WLAN_AUTH_FT) mlme_deletekeys_request(hapd, sta); + ap_sta_clear_disconnect_timeouts(hapd, sta); } diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 934dcfc8d6318..cdb49cdd9d32f 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -173,6 +173,8 @@ int authsrv_init(struct hostapd_data *hapd) params.openssl_ciphers = hapd->conf->openssl_ciphers; params.ocsp_stapling_response = hapd->conf->ocsp_stapling_response; + params.ocsp_stapling_response_multi = + hapd->conf->ocsp_stapling_response_multi; if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); @@ -193,6 +195,7 @@ int authsrv_init(struct hostapd_data *hapd) if (hapd->conf->eap_sim_db) { hapd->eap_sim_db_priv = eap_sim_db_init(hapd->conf->eap_sim_db, + hapd->conf->eap_sim_db_timeout, hostapd_sim_db_cb, hapd); if (hapd->eap_sim_db_priv == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM " diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 5fe8fd5660b41..233320d2e978c 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -29,6 +29,7 @@ #include "beacon.h" #include "hs20.h" #include "dfs.h" +#include "taxonomy.h" #ifdef NEED_AP_MLME @@ -36,18 +37,21 @@ static u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid, size_t len) { - if (!hapd->conf->radio_measurements || len < 2 + 4) + size_t i; + + for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) { + if (hapd->conf->radio_measurements[i]) + break; + } + + if (i == RRM_CAPABILITIES_IE_LEN || len < 2 + RRM_CAPABILITIES_IE_LEN) return eid; *eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES; - *eid++ = 5; - *eid++ = (hapd->conf->radio_measurements & BIT(0)) ? - WLAN_RRM_CAPS_NEIGHBOR_REPORT : 0x00; - *eid++ = 0x00; - *eid++ = 0x00; - *eid++ = 0x00; - *eid++ = 0x00; - return eid; + *eid++ = RRM_CAPABILITIES_IE_LEN; + os_memcpy(eid, hapd->conf->radio_measurements, RRM_CAPABILITIES_IE_LEN); + + return eid + RRM_CAPABILITIES_IE_LEN; } @@ -297,65 +301,65 @@ static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len) static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid) { - u8 chan; - - if (!hapd->cs_freq_params.freq) +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->iface->cs_oper_class && hapd->iconf->ecsa_ie_only) return eid; +#endif /* CONFIG_TESTING_OPTIONS */ - if (ieee80211_freq_to_chan(hapd->cs_freq_params.freq, &chan) == - NUM_HOSTAPD_MODES) + if (!hapd->cs_freq_params.channel) return eid; *eid++ = WLAN_EID_CHANNEL_SWITCH; *eid++ = 3; *eid++ = hapd->cs_block_tx; - *eid++ = chan; + *eid++ = hapd->cs_freq_params.channel; *eid++ = hapd->cs_count; return eid; } -static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid) +static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid) { - u8 sec_ch; - - if (!hapd->cs_freq_params.sec_channel_offset) + if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class) return eid; - if (hapd->cs_freq_params.sec_channel_offset == -1) - sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW; - else if (hapd->cs_freq_params.sec_channel_offset == 1) - sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE; - else - return eid; - - *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; - *eid++ = 1; - *eid++ = sec_ch; + *eid++ = WLAN_EID_EXT_CHANSWITCH_ANN; + *eid++ = 4; + *eid++ = hapd->cs_block_tx; + *eid++ = hapd->iface->cs_oper_class; + *eid++ = hapd->cs_freq_params.channel; + *eid++ = hapd->cs_count; return eid; } -static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos, - u8 *start, unsigned int *csa_counter_off) +static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid) { - u8 *old_pos = pos; + u8 op_class, channel; - if (!csa_counter_off) - return pos; + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || + !hapd->iface->freq) + return eid; - *csa_counter_off = 0; - pos = hostapd_eid_csa(hapd, pos); + 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 eid; - if (pos != old_pos) { - /* save an offset to the counter - should be last byte */ - *csa_counter_off = pos - start - 1; - pos = hostapd_eid_secondary_channel(hapd, pos); - } + *eid++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES; + *eid++ = 2; - return pos; + /* Current Operating Class */ + *eid++ = op_class; + + /* TODO: Advertise all the supported operating classes */ + *eid++ = 0; + + return eid; } @@ -364,7 +368,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, int is_p2p, size_t *resp_len) { struct ieee80211_mgmt *resp; - u8 *pos, *epos; + u8 *pos, *epos, *csa_pos; size_t buflen; #define MAX_PROBERESP_LEN 768 @@ -387,6 +391,9 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + 2 + sizeof(struct ieee80211_vht_operation); } + + buflen += hostapd_mbo_ie_len(hapd); + resp = os_zalloc(buflen); if (resp == NULL) return NULL; @@ -424,6 +431,12 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, /* Power Constraint element */ pos = hostapd_eid_pwr_constraint(hapd, pos); + /* CSA IE */ + csa_pos = hostapd_eid_csa(hapd, pos); + if (csa_pos != pos) + hapd->cs_c_off_proberesp = csa_pos - (u8 *) resp - 1; + pos = csa_pos; + /* ERP Information element */ pos = hostapd_eid_erp_info(hapd, pos); @@ -437,7 +450,19 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos); + /* eCSA IE */ + csa_pos = hostapd_eid_ecsa(hapd, pos); + if (csa_pos != pos) + hapd->cs_c_off_ecsa_proberesp = csa_pos - (u8 *) resp - 1; + pos = csa_pos; + + pos = hostapd_eid_supported_op_classes(hapd, pos); + #ifdef CONFIG_IEEE80211N + /* Secondary Channel Offset element */ + /* TODO: The standard doesn't specify a position for this element. */ + pos = hostapd_eid_secondary_channel(hapd, pos); + pos = hostapd_eid_ht_capabilities(hapd, pos); pos = hostapd_eid_ht_operation(hapd, pos); #endif /* CONFIG_IEEE80211N */ @@ -451,9 +476,6 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_adv_proto(hapd, pos); pos = hostapd_eid_roaming_consortium(hapd, pos); - pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp, - &hapd->cs_c_off_proberesp); - #ifdef CONFIG_FST if (hapd->iface->fst_ies) { os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies), @@ -464,8 +486,10 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { - pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_capabilities(hapd, pos, 0); pos = hostapd_eid_vht_operation(hapd, pos); + pos = hostapd_eid_txpower_envelope(hapd, pos); + pos = hostapd_eid_wb_chsw_wrapper(hapd, pos); } if (hapd->conf->vendor_vht) pos = hostapd_eid_vendor_vht(hapd, pos); @@ -501,6 +525,8 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_osen(hapd, pos); #endif /* CONFIG_HS20 */ + pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos); + if (hapd->conf->vendor_elements) { os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), wpabuf_len(hapd->conf->vendor_elements)); @@ -537,8 +563,8 @@ static enum ssid_match_result ssid_match(struct hostapd_data *hapd, pos = ssid_list; end = ssid_list + ssid_list_len; - while (pos + 1 <= end) { - if (pos + 2 + pos[1] > end) + while (end - pos >= 1) { + if (2 + pos[1] > end - pos) break; if (pos[1] == 0) wildcard = 1; @@ -574,7 +600,7 @@ void sta_track_expire(struct hostapd_iface *iface, int force) MAC2STR(info->addr)); dl_list_del(&info->list); iface->num_sta_seen--; - os_free(info); + sta_track_del(info); } } @@ -607,6 +633,8 @@ void sta_track_add(struct hostapd_iface *iface, const u8 *addr) /* Add a new entry */ info = os_zalloc(sizeof(*info)); + if (info == NULL) + return; os_memcpy(info->addr, addr, ETH_ALEN); os_get_reltime(&info->last_seen); @@ -648,6 +676,23 @@ sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, } +#ifdef CONFIG_TAXONOMY +void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr, + struct wpabuf **probe_ie_taxonomy) +{ + struct hostapd_sta_info *info; + + info = sta_track_get(iface, addr); + if (!info) + return; + + wpabuf_free(*probe_ie_taxonomy); + *probe_ie_taxonomy = info->probe_ie_taxonomy; + info->probe_ie_taxonomy = NULL; +} +#endif /* CONFIG_TAXONOMY */ + + void handle_probe_req(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int ssi_signal) @@ -659,13 +704,16 @@ void handle_probe_req(struct hostapd_data *hapd, size_t i, resp_len; int noack; enum ssid_match_result res; + int ret; + u16 csa_offs[2]; + size_t csa_offs_len; - ie = mgmt->u.probe_req.variable; - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + if (len < IEEE80211_HDRLEN) return; + ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN; if (hapd->iconf->track_sta_max_num) sta_track_add(hapd->iface, mgmt->sa); - ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + ie_len = len - IEEE80211_HDRLEN; for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, @@ -711,7 +759,7 @@ void handle_probe_req(struct hostapd_data *hapd, } #ifdef CONFIG_P2P - if (hapd->p2p && elems.wps_ie) { + if (hapd->p2p && hapd->p2p_group && elems.wps_ie) { struct wpabuf *wps; wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) { @@ -724,7 +772,7 @@ void handle_probe_req(struct hostapd_data *hapd, wpabuf_free(wps); } - if (hapd->p2p && elems.p2p) { + if (hapd->p2p && hapd->p2p_group && elems.p2p) { struct wpabuf *p2p; p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE); if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) { @@ -754,6 +802,21 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_TAXONOMY + { + struct sta_info *sta; + struct hostapd_sta_info *info; + + if ((sta = ap_get_sta(hapd, mgmt->sa)) != NULL) { + taxonomy_sta_info_probe_req(hapd, sta, ie, ie_len); + } else if ((info = sta_track_get(hapd->iface, + mgmt->sa)) != NULL) { + taxonomy_hostapd_sta_info_probe_req(hapd, info, + ie, ie_len); + } + } +#endif /* CONFIG_TAXONOMY */ + res = ssid_match(hapd, elems.ssid, elems.ssid_len, elems.ssid_list, elems.ssid_list_len); if (res == NO_SSID_MATCH) { @@ -825,6 +888,17 @@ void handle_probe_req(struct hostapd_data *hapd, return; } + if (hapd->conf->no_probe_resp_if_max_sta && + is_multicast_ether_addr(mgmt->da) && + is_multicast_ether_addr(mgmt->bssid) && + hapd->num_sta >= hapd->conf->max_num_sta && + !ap_get_sta(hapd, mgmt->sa)) { + wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR + " since no room for additional STA", + hapd->conf->iface, MAC2STR(mgmt->sa)); + return; + } + #ifdef CONFIG_TESTING_OPTIONS if (hapd->iconf->ignore_probe_probability > 0.0 && drand48() < hapd->iconf->ignore_probe_probability) { @@ -847,7 +921,22 @@ void handle_probe_req(struct hostapd_data *hapd, noack = !!(res == WILDCARD_SSID_MATCH && is_broadcast_ether_addr(mgmt->da)); - if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0) + csa_offs_len = 0; + if (hapd->csa_in_progress) { + if (hapd->cs_c_off_proberesp) + csa_offs[csa_offs_len++] = + hapd->cs_c_off_proberesp; + + if (hapd->cs_c_off_ecsa_proberesp) + csa_offs[csa_offs_len++] = + hapd->cs_c_off_ecsa_proberesp; + } + + ret = hostapd_drv_send_mlme_csa(hapd, resp, resp_len, noack, + csa_offs_len ? csa_offs : NULL, + csa_offs_len); + + if (ret < 0) wpa_printf(MSG_INFO, "handle_probe_req: send failed"); os_free(resp); @@ -896,6 +985,16 @@ static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, #endif /* NEED_AP_MLME */ +void sta_track_del(struct hostapd_sta_info *info) +{ +#ifdef CONFIG_TAXONOMY + wpabuf_free(info->probe_ie_taxonomy); + info->probe_ie_taxonomy = NULL; +#endif /* CONFIG_TAXONOMY */ + os_free(info); +} + + int ieee802_11_build_ap_params(struct hostapd_data *hapd, struct wpa_driver_ap_params *params) { @@ -906,7 +1005,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, size_t resp_len = 0; #ifdef NEED_AP_MLME u16 capab_info; - u8 *pos, *tailpos; + u8 *pos, *tailpos, *csa_pos; #define BEACON_HEAD_BUF_SIZE 256 #define BEACON_TAIL_BUF_SIZE 512 @@ -934,6 +1033,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211AC */ + tail_len += hostapd_mbo_ie_len(hapd); + tailpos = tail = os_malloc(tail_len); if (head == NULL || tail == NULL) { wpa_printf(MSG_ERROR, "Failed to set beacon data"); @@ -987,6 +1088,12 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, /* Power Constraint element */ tailpos = hostapd_eid_pwr_constraint(hapd, tailpos); + /* CSA IE */ + csa_pos = hostapd_eid_csa(hapd, tailpos); + if (csa_pos != tailpos) + hapd->cs_c_off_beacon = csa_pos - tail - 1; + tailpos = csa_pos; + /* ERP Information element */ tailpos = hostapd_eid_erp_info(hapd, tailpos); @@ -1004,7 +1111,19 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tailpos = hostapd_eid_bss_load(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos); + /* eCSA IE */ + csa_pos = hostapd_eid_ecsa(hapd, tailpos); + if (csa_pos != tailpos) + hapd->cs_c_off_ecsa_beacon = csa_pos - tail - 1; + tailpos = csa_pos; + + tailpos = hostapd_eid_supported_op_classes(hapd, tailpos); + #ifdef CONFIG_IEEE80211N + /* Secondary Channel Offset element */ + /* TODO: The standard doesn't specify a position for this element. */ + tailpos = hostapd_eid_secondary_channel(hapd, tailpos); + tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); tailpos = hostapd_eid_ht_operation(hapd, tailpos); #endif /* CONFIG_IEEE80211N */ @@ -1020,8 +1139,6 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tailpos = hostapd_eid_interworking(hapd, tailpos); tailpos = hostapd_eid_adv_proto(hapd, tailpos); tailpos = hostapd_eid_roaming_consortium(hapd, tailpos); - tailpos = hostapd_add_csa_elems(hapd, tailpos, tail, - &hapd->cs_c_off_beacon); #ifdef CONFIG_FST if (hapd->iface->fst_ies) { @@ -1033,8 +1150,10 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { - tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); + tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0); tailpos = hostapd_eid_vht_operation(hapd, tailpos); + tailpos = hostapd_eid_txpower_envelope(hapd, tailpos); + tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos); } if (hapd->conf->vendor_vht) tailpos = hostapd_eid_vendor_vht(hapd, tailpos); @@ -1069,6 +1188,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tailpos = hostapd_eid_osen(hapd, tailpos); #endif /* CONFIG_HS20 */ + tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos); + if (hapd->conf->vendor_elements) { os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), wpabuf_len(hapd->conf->vendor_elements)); @@ -1153,6 +1274,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, params->osen = 1; } #endif /* CONFIG_HS20 */ + params->pbss = hapd->conf->pbss; return 0; } diff --git a/src/ap/beacon.h b/src/ap/beacon.h index d98f42e8157ae..fc711815cf65e 100644 --- a/src/ap/beacon.h +++ b/src/ap/beacon.h @@ -22,9 +22,12 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, struct wpa_driver_ap_params *params); void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params); void sta_track_add(struct hostapd_iface *iface, const u8 *addr); +void sta_track_del(struct hostapd_sta_info *info); void sta_track_expire(struct hostapd_iface *iface, int force); struct hostapd_data * sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr, const char *ifname); +void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr, + struct wpabuf **probe_ie_taxonomy); #endif /* BEACON_H */ diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index c98978f33d05e..3680fda3153f5 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -22,6 +22,8 @@ #include "p2p_hostapd.h" #include "ctrl_iface_ap.h" #include "ap_drv_ops.h" +#include "mbo_ap.h" +#include "taxonomy.h" static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, @@ -35,9 +37,9 @@ static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, return 0; ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n" - "rx_bytes=%lu\ntx_bytes=%lu\n", + "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n", data.rx_packets, data.tx_packets, - data.rx_bytes, data.tx_bytes); + data.rx_bytes, data.tx_bytes, data.inactive_msec); if (os_snprintf_error(buflen, ret)) return 0; return ret; @@ -161,6 +163,19 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, len += res; } + res = mbo_ap_get_info(sta, buf + len, buflen - len); + if (res >= 0) + len += res; + + if (sta->supp_op_classes && + buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) { + len += os_snprintf(buf + len, buflen - len, "supp_op_classes="); + len += wpa_snprintf_hex(buf + len, buflen - len, + sta->supp_op_classes + 1, + sta->supp_op_classes[0]); + len += os_snprintf(buf + len, buflen - len, "\n"); + } + return len; } @@ -244,7 +259,7 @@ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, int ret; u8 *pos; - if (hapd->driver->send_frame == NULL) + if (!hapd->drv_priv || !hapd->driver->send_frame) return -1; mgmt = os_zalloc(sizeof(*mgmt) + 100); @@ -255,7 +270,7 @@ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR " with minor reason code %u (stype=%u (%s))", MAC2STR(addr), minor_reason_code, stype, - fc2str(mgmt->frame_control)); + fc2str(le_to_host16(mgmt->frame_control))); os_memcpy(mgmt->da, addr, ETH_ALEN); os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); @@ -311,7 +326,7 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, if (pos) { struct ieee80211_mgmt mgmt; int encrypt; - if (hapd->driver->send_frame == NULL) + if (!hapd->drv_priv || !hapd->driver->send_frame) return -1; pos += 6; encrypt = atoi(pos); @@ -338,7 +353,10 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, } #endif /* CONFIG_P2P_MANAGER */ - hostapd_drv_sta_deauth(hapd, addr, reason); + if (os_strstr(txtaddr, " tx=0")) + hostapd_drv_sta_remove(hapd, addr); + else + hostapd_drv_sta_deauth(hapd, addr, reason); sta = ap_get_sta(hapd, addr); if (sta) ap_sta_deauthenticate(hapd, sta, reason); @@ -371,7 +389,7 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, if (pos) { struct ieee80211_mgmt mgmt; int encrypt; - if (hapd->driver->send_frame == NULL) + if (!hapd->drv_priv || !hapd->driver->send_frame) return -1; pos += 6; encrypt = atoi(pos); @@ -398,7 +416,10 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, } #endif /* CONFIG_P2P_MANAGER */ - hostapd_drv_sta_disassoc(hapd, addr, reason); + if (os_strstr(txtaddr, " tx=0")) + hostapd_drv_sta_remove(hapd, addr); + else + hostapd_drv_sta_disassoc(hapd, addr, reason); sta = ap_get_sta(hapd, addr); if (sta) ap_sta_disassociate(hapd, sta, reason); @@ -409,6 +430,49 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, } +#ifdef CONFIG_TAXONOMY +int hostapd_ctrl_iface_signature(struct hostapd_data *hapd, + const char *txtaddr, + char *buf, size_t buflen) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE SIGNATURE %s", txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta) + return -1; + + return retrieve_sta_taxonomy(hapd, sta, buf, buflen); +} +#endif /* CONFIG_TAXONOMY */ + + +int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE POLL_STA %s", txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta) + return -1; + + hostapd_drv_poll_client(hapd, hapd->own_addr, addr, + sta->flags & WLAN_STA_WMM); + return 0; +} + + int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, size_t buflen) { @@ -473,20 +537,28 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, "channel=%u\n" "secondary_channel=%d\n" "ieee80211n=%d\n" - "ieee80211ac=%d\n" - "vht_oper_chwidth=%d\n" - "vht_oper_centr_freq_seg0_idx=%d\n" - "vht_oper_centr_freq_seg1_idx=%d\n", + "ieee80211ac=%d\n", iface->conf->channel, - iface->conf->secondary_channel, - iface->conf->ieee80211n, - iface->conf->ieee80211ac, - iface->conf->vht_oper_chwidth, - iface->conf->vht_oper_centr_freq_seg0_idx, - iface->conf->vht_oper_centr_freq_seg1_idx); + iface->conf->ieee80211n && !hapd->conf->disable_11n ? + iface->conf->secondary_channel : 0, + iface->conf->ieee80211n && !hapd->conf->disable_11n, + iface->conf->ieee80211ac && + !hapd->conf->disable_11ac); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; + if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) { + ret = os_snprintf(buf + len, buflen - len, + "vht_oper_chwidth=%d\n" + "vht_oper_centr_freq_seg0_idx=%d\n" + "vht_oper_centr_freq_seg1_idx=%d\n", + iface->conf->vht_oper_chwidth, + iface->conf->vht_oper_centr_freq_seg0_idx, + iface->conf->vht_oper_centr_freq_seg1_idx); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } for (i = 0; i < iface->num_bss; i++) { struct hostapd_data *bss = iface->bss[i]; @@ -554,3 +626,16 @@ int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd) { return hostapd_drv_stop_ap(hapd); } + + +int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, + size_t len) +{ + return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len); +} + + +void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd) +{ + wpa_auth_pmksa_flush(hapd->wpa_auth); +} diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h index e5297d03e810a..4f996800f1326 100644 --- a/src/ap/ctrl_iface_ap.h +++ b/src/ap/ctrl_iface_ap.h @@ -19,10 +19,18 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, const char *txtaddr); int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, const char *txtaddr); +int hostapd_ctrl_iface_signature(struct hostapd_data *hapd, + const char *txtaddr, + char *buf, size_t buflen); +int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd, + const char *txtaddr); int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, size_t buflen); int hostapd_parse_csa_settings(const char *pos, struct csa_settings *settings); int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd); +int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, + size_t len); +void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd); #endif /* CTRL_IFACE_AP_H */ diff --git a/src/ap/dfs.c b/src/ap/dfs.c index 715f19b6ac7bd..47adba7ef7261 100644 --- a/src/ap/dfs.c +++ b/src/ap/dfs.c @@ -450,7 +450,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface, return NULL; if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) - _rand = os_random(); + return NULL; chan_idx = _rand % num_available_chandefs; dfs_find_channel(iface, &chan, chan_idx, skip_radar); @@ -704,7 +704,8 @@ int hostapd_handle_dfs(struct hostapd_iface *iface) skip_radar); if (!channel) { wpa_printf(MSG_ERROR, "could not get valid channel"); - return -1; + hostapd_set_state(iface, HAPD_IFACE_DFS); + return 0; } iface->freq = channel->freq; @@ -793,7 +794,6 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) if (!channel) { wpa_printf(MSG_ERROR, "No valid channel available"); - hostapd_setup_interface_complete(iface, err); return err; } @@ -817,16 +817,6 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) } -static int hostapd_csa_in_progress(struct hostapd_iface *iface) -{ - unsigned int i; - for (i = 0; i < iface->num_bss; i++) - if (iface->bss[i]->csa_in_progress) - return 1; - return 0; -} - - static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) { struct hostapd_channel_data *channel; @@ -868,8 +858,9 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) &vht_oper_centr_freq_seg1_idx, skip_radar); if (!channel) { - /* FIXME: Wait for channel(s) to become available */ - hostapd_disable_iface(iface); + wpa_printf(MSG_INFO, + "%s: no DFS channels left, waiting for NOP to finish", + __func__); return err; } @@ -992,6 +983,11 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, /* TODO add correct implementation here */ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); + + /* Handle cases where all channels were initially unavailable */ + if (iface->state == HAPD_IFACE_DFS && !iface->cac_started) + hostapd_handle_dfs(iface); + return 0; } diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c index 3a77225f380e3..f0212fb2a984b 100644 --- a/src/ap/dhcp_snoop.c +++ b/src/ap/dhcp_snoop.c @@ -121,7 +121,8 @@ static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR " @ IPv4 address %s/%d", - MAC2STR(sta->addr), ipaddr_str(ntohl(b->your_ip)), + MAC2STR(sta->addr), + ipaddr_str(be_to_host32(b->your_ip)), prefixlen); if (sta->ipaddr == b->your_ip) diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index ca8b75c839060..3552b3e0d53b6 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -22,6 +22,7 @@ #include "wnm_ap.h" #include "hostapd.h" #include "ieee802_11.h" +#include "ieee802_11_auth.h" #include "sta_info.h" #include "accounting.h" #include "tkip_countermeasures.h" @@ -33,6 +34,7 @@ #include "hw_features.h" #include "dfs.h" #include "beacon.h" +#include "mbo_ap.h" int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, @@ -114,6 +116,21 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, } sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); + /* + * ACL configurations to the drivers (implementing AP SME and ACL + * offload) without hostapd's knowledge, can result in a disconnection + * though the driver accepts the connection. Skip the hostapd check for + * ACL if the driver supports ACL offload to avoid potentially + * conflicting ACL rules. + */ + if (hapd->iface->drv_max_acl_mac_addrs == 0 && + hostapd_check_acl(hapd, addr, NULL) != HOSTAPD_ACL_ACCEPT) { + wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect", + MAC2STR(addr)); + reason = WLAN_REASON_UNSPECIFIED; + goto fail; + } + #ifdef CONFIG_P2P if (elems.p2p) { wpabuf_free(sta->p2p_ie); @@ -164,6 +181,11 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->mb_ies = NULL; #endif /* CONFIG_FST */ + mbo_ap_check_sta_assoc(hapd, sta, &elems); + + ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes, + elems.supp_op_classes_len); + if (hapd->conf->wpa) { if (ie == NULL || ielen == 0) { #ifdef CONFIG_WPS @@ -338,6 +360,17 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, return WLAN_STATUS_INVALID_IE; #endif /* CONFIG_HS20 */ } + +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) && + elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) && + hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + wpa_printf(MSG_INFO, + "MBO: Reject WPA2 association without PMF"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } +#endif /* CONFIG_MBO */ + #ifdef CONFIG_WPS skip_wpa_check: #endif /* CONFIG_WPS */ @@ -447,7 +480,8 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, int offset, int width, int cf1, int cf2) { #ifdef NEED_AP_MLME - int channel, chwidth, seg0_idx = 0, seg1_idx = 0, is_dfs; + int channel, chwidth, is_dfs; + u8 seg0_idx = 0, seg1_idx = 0; hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, @@ -491,8 +525,8 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, seg1_idx = (cf2 - 5000) / 5; break; default: - seg0_idx = hostapd_hw_get_channel(hapd, cf1); - seg1_idx = hostapd_hw_get_channel(hapd, cf2); + ieee80211_freq_to_chan(cf1, &seg0_idx); + ieee80211_freq_to_chan(cf2, &seg1_idx); break; } @@ -539,10 +573,11 @@ void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, #ifdef CONFIG_ACS -static void hostapd_acs_channel_selected(struct hostapd_data *hapd, - struct acs_selected_channels *acs_res) +void hostapd_acs_channel_selected(struct hostapd_data *hapd, + struct acs_selected_channels *acs_res) { int ret, i; + int err = 0; if (hapd->iconf->channel) { wpa_printf(MSG_INFO, "ACS: Channel was already set to %d", @@ -564,7 +599,8 @@ static void hostapd_acs_channel_selected(struct hostapd_data *hapd, hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "driver selected to bad hw_mode"); - return; + err = 1; + goto out; } } @@ -574,7 +610,8 @@ static void hostapd_acs_channel_selected(struct hostapd_data *hapd, hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_WARNING, "driver switched to bad channel"); - return; + err = 1; + goto out; } hapd->iconf->channel = acs_res->pri_channel; @@ -588,7 +625,8 @@ static void hostapd_acs_channel_selected(struct hostapd_data *hapd, hapd->iconf->secondary_channel = 1; else { wpa_printf(MSG_ERROR, "Invalid secondary channel!"); - return; + err = 1; + goto out; } if (hapd->iface->conf->ieee80211ac) { @@ -617,7 +655,8 @@ static void hostapd_acs_channel_selected(struct hostapd_data *hapd, } } - ret = hostapd_acs_completed(hapd->iface, 0); +out: + ret = hostapd_acs_completed(hapd->iface, err); if (ret) { wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid"); @@ -884,11 +923,24 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, u16 stype, int ok) { struct ieee80211_hdr *hdr; + struct hostapd_data *orig_hapd = hapd; hdr = (struct ieee80211_hdr *) buf; hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len)); - if (hapd == NULL || hapd == HAPD_BROADCAST) + if (!hapd) return; + if (hapd == HAPD_BROADCAST) { + if (stype != WLAN_FC_STYPE_ACTION || len <= 25 || + buf[24] != WLAN_ACTION_PUBLIC) + return; + hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2); + if (!hapd || hapd == HAPD_BROADCAST) + return; + /* + * Allow processing of TX status for a Public Action frame that + * used wildcard BBSID. + */ + } ieee802_11_mgmt_cb(hapd, buf, len, stype, ok); } @@ -935,6 +987,8 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src, ieee802_1x_receive(hapd, src, data, data_len); } +#endif /* HOSTAPD */ + static struct hostapd_channel_data * hostapd_get_mode_channel( struct hostapd_iface *iface, unsigned int freq) @@ -944,8 +998,6 @@ static struct hostapd_channel_data * hostapd_get_mode_channel( for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; - if (!chan) - return NULL; if ((unsigned int) chan->freq == freq) return chan; } @@ -1009,10 +1061,9 @@ static void hostapd_single_channel_get_survey(struct hostapd_iface *iface, } -static void hostapd_event_get_survey(struct hostapd_data *hapd, - struct survey_results *survey_results) +void hostapd_event_get_survey(struct hostapd_iface *iface, + struct survey_results *survey_results) { - struct hostapd_iface *iface = hapd->iface; struct freq_survey *survey, *tmp; struct hostapd_channel_data *chan; @@ -1044,6 +1095,7 @@ static void hostapd_event_get_survey(struct hostapd_data *hapd, } +#ifdef HOSTAPD #ifdef NEED_AP_MLME static void hostapd_event_iface_unavailable(struct hostapd_data *hapd) @@ -1251,7 +1303,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->connect_failed_reason.code); break; case EVENT_SURVEY: - hostapd_event_get_survey(hapd, &data->survey_results); + hostapd_event_get_survey(hapd->iface, &data->survey_results); break; #ifdef NEED_AP_MLME case EVENT_INTERFACE_UNAVAILABLE: @@ -1321,4 +1373,31 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } } + +void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event, + union wpa_event_data *data) +{ + struct hapd_interfaces *interfaces = ctx; + struct hostapd_data *hapd; + + if (event != EVENT_INTERFACE_STATUS) + return; + + hapd = hostapd_get_iface(interfaces, data->interface_status.ifname); + if (hapd && hapd->driver && hapd->driver->get_ifindex && + hapd->drv_priv) { + unsigned int ifindex; + + ifindex = hapd->driver->get_ifindex(hapd->drv_priv); + if (ifindex != data->interface_status.ifindex) { + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "interface status ifindex %d mismatch (%d)", + ifindex, data->interface_status.ifindex); + return; + } + } + if (hapd) + wpa_supplicant_event(hapd, event, data); +} + #endif /* HOSTAPD */ diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c index 9d19f98d0b7c7..6ce178de3b294 100644 --- a/src/ap/gas_serv.c +++ b/src/ap/gas_serv.c @@ -101,6 +101,7 @@ gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr, if (sta->gas_dialog[i].dialog_token != dialog_token || !sta->gas_dialog[i].valid) continue; + ap_sta_replenish_timeout(hapd, sta, 5); return &sta->gas_dialog[i]; } wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for " @@ -167,27 +168,107 @@ static void anqp_add_hs_capab_list(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ +static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd, + u16 infoid) +{ + struct anqp_element *elem; + + dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element, + list) { + if (elem->infoid == infoid) + return elem; + } + + return NULL; +} + + +static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf, + u16 infoid) +{ + struct anqp_element *elem; + + elem = get_anqp_elem(hapd, infoid); + if (!elem) + return; + if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) { + wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload", + infoid); + return; + } + + wpabuf_put_le16(buf, infoid); + wpabuf_put_le16(buf, wpabuf_len(elem->payload)); + wpabuf_put_buf(buf, elem->payload); +} + + +static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf, + u16 infoid) +{ + if (get_anqp_elem(hapd, infoid)) { + anqp_add_elem(hapd, buf, infoid); + return 1; + } + + return 0; +} + + static void anqp_add_capab_list(struct hostapd_data *hapd, struct wpabuf *buf) { u8 *len; + u16 id; + + if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST)) + return; len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST); wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST); - if (hapd->conf->venue_name) + if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME)) wpabuf_put_le16(buf, ANQP_VENUE_NAME); - if (hapd->conf->network_auth_type) + if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER)) + wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER); + if (hapd->conf->network_auth_type || + get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE)) wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); - if (hapd->conf->roaming_consortium) + if (hapd->conf->roaming_consortium || + get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM)) wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM); - if (hapd->conf->ipaddr_type_configured) + if (hapd->conf->ipaddr_type_configured || + get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY)) wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); - if (hapd->conf->nai_realm_data) + if (hapd->conf->nai_realm_data || + get_anqp_elem(hapd, ANQP_NAI_REALM)) wpabuf_put_le16(buf, ANQP_NAI_REALM); - if (hapd->conf->anqp_3gpp_cell_net) + if (hapd->conf->anqp_3gpp_cell_net || + get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK)) wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); - if (hapd->conf->domain_name) + if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION)) + wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION); + if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION)) + wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION); + if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI)) + wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI); + if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME)) wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); + if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI)) + wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI); + if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI)) + wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI); + if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT)) + wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT); + for (id = 273; id < 277; id++) { + if (get_anqp_elem(hapd, id)) + wpabuf_put_le16(buf, id); + } + if (get_anqp_elem(hapd, ANQP_VENUE_URL)) + wpabuf_put_le16(buf, ANQP_VENUE_URL); + if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE)) + wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE); + if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT)) + wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT); #ifdef CONFIG_HS20 anqp_add_hs_capab_list(hapd, buf); #endif /* CONFIG_HS20 */ @@ -197,6 +278,9 @@ static void anqp_add_capab_list(struct hostapd_data *hapd, static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) { + if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME)) + return; + if (hapd->conf->venue_name) { u8 *len; unsigned int i; @@ -218,6 +302,9 @@ static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) static void anqp_add_network_auth_type(struct hostapd_data *hapd, struct wpabuf *buf) { + if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE)) + return; + if (hapd->conf->network_auth_type) { wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); wpabuf_put_le16(buf, hapd->conf->network_auth_type_len); @@ -233,6 +320,9 @@ static void anqp_add_roaming_consortium(struct hostapd_data *hapd, unsigned int i; u8 *len; + if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM)) + return; + len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM); for (i = 0; i < hapd->conf->roaming_consortium_count; i++) { struct hostapd_roaming_consortium *rc; @@ -247,6 +337,9 @@ static void anqp_add_roaming_consortium(struct hostapd_data *hapd, static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd, struct wpabuf *buf) { + if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY)) + return; + if (hapd->conf->ipaddr_type_configured) { wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); wpabuf_put_le16(buf, 1); @@ -309,7 +402,7 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, pos = home_realm; end = pos + home_realm_len; - if (pos + 1 > end) { + if (end - pos < 1) { wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query", home_realm, home_realm_len); return -1; @@ -317,7 +410,7 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, num_realms = *pos++; for (i = 0; i < num_realms && num_matching < 10; i++) { - if (pos + 2 > end) { + if (end - pos < 2) { wpa_hexdump(MSG_DEBUG, "Truncated NAI Home Realm Query", home_realm, home_realm_len); @@ -325,7 +418,7 @@ static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, } encoding = *pos++; realm_len = *pos++; - if (pos + realm_len > end) { + if (realm_len > end - pos) { wpa_hexdump(MSG_DEBUG, "Truncated NAI Home Realm Query", home_realm, home_realm_len); @@ -391,6 +484,10 @@ static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, const u8 *home_realm, size_t home_realm_len, int nai_realm, int nai_home_realm) { + if (nai_realm && !nai_home_realm && + anqp_add_override(hapd, buf, ANQP_NAI_REALM)) + return; + if (nai_realm && hapd->conf->nai_realm_data) { u8 *len; unsigned int i, j; @@ -424,6 +521,9 @@ static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd, struct wpabuf *buf) { + if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK)) + return; + if (hapd->conf->anqp_3gpp_cell_net) { wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); wpabuf_put_le16(buf, @@ -436,6 +536,9 @@ static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd, static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) { + if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME)) + return; + if (hapd->conf->domain_name) { wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); wpabuf_put_le16(buf, hapd->conf->domain_name_len); @@ -683,20 +786,42 @@ static void anqp_add_icon_binary_file(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ +static size_t anqp_get_required_len(struct hostapd_data *hapd, + const u16 *infoid, + unsigned int num_infoid) +{ + size_t len = 0; + unsigned int i; + + for (i = 0; i < num_infoid; i++) { + struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]); + + if (elem) + len += 2 + 2 + wpabuf_len(elem->payload); + } + + return len; +} + + static struct wpabuf * gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, unsigned int request, const u8 *home_realm, size_t home_realm_len, - const u8 *icon_name, size_t icon_name_len) + const u8 *icon_name, size_t icon_name_len, + const u16 *extra_req, + unsigned int num_extra_req) { struct wpabuf *buf; size_t len; + unsigned int i; len = 1400; if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) len += 1000; if (request & ANQP_REQ_ICON_REQUEST) len += 65536; + len += anqp_get_required_len(hapd, extra_req, num_extra_req); buf = wpabuf_alloc(len); if (buf == NULL) @@ -706,6 +831,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, anqp_add_capab_list(hapd, buf); if (request & ANQP_REQ_VENUE_NAME) anqp_add_venue_name(hapd, buf); + if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER) + anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER); if (request & ANQP_REQ_NETWORK_AUTH_TYPE) anqp_add_network_auth_type(hapd, buf); if (request & ANQP_REQ_ROAMING_CONSORTIUM) @@ -718,8 +845,23 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, request & ANQP_REQ_NAI_HOME_REALM); if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK) anqp_add_3gpp_cellular_network(hapd, buf); + if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION) + anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION); + if (request & ANQP_REQ_AP_CIVIC_LOCATION) + anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION); + if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI) + anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI); if (request & ANQP_REQ_DOMAIN_NAME) anqp_add_domain_name(hapd, buf); + if (request & ANQP_REQ_EMERGENCY_ALERT_URI) + anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI); + if (request & ANQP_REQ_TDLS_CAPABILITY) + anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY); + if (request & ANQP_REQ_EMERGENCY_NAI) + anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI); + + for (i = 0; i < num_extra_req; i++) + anqp_add_elem(hapd, buf, extra_req[i]); #ifdef CONFIG_HS20 if (request & ANQP_REQ_HS_CAPABILITY_LIST) @@ -742,6 +884,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, } +#define ANQP_MAX_EXTRA_REQ 20 + struct anqp_query_info { unsigned int request; const u8 *home_realm_query; @@ -749,6 +893,8 @@ struct anqp_query_info { const u8 *icon_name; size_t icon_name_len; int p2p_sd; + u16 extra_req[ANQP_MAX_EXTRA_REQ]; + unsigned int num_extra_req; }; @@ -776,6 +922,11 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id, set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name", hapd->conf->venue_name != NULL, qi); break; + case ANQP_EMERGENCY_CALL_NUMBER: + set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER, + "Emergency Call Number", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; case ANQP_NETWORK_AUTH_TYPE: set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type", hapd->conf->network_auth_type != NULL, qi); @@ -798,13 +949,55 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id, "3GPP Cellular Network", hapd->conf->anqp_3gpp_cell_net != NULL, qi); break; + case ANQP_AP_GEOSPATIAL_LOCATION: + set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION, + "AP Geospatial Location", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; + case ANQP_AP_CIVIC_LOCATION: + set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION, + "AP Civic Location", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; + case ANQP_AP_LOCATION_PUBLIC_URI: + set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI, + "AP Location Public URI", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; case ANQP_DOMAIN_NAME: set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name", hapd->conf->domain_name != NULL, qi); break; + case ANQP_EMERGENCY_ALERT_URI: + set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI, + "Emergency Alert URI", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; + case ANQP_TDLS_CAPABILITY: + set_anqp_req(ANQP_REQ_TDLS_CAPABILITY, + "TDLS Capability", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; + case ANQP_EMERGENCY_NAI: + set_anqp_req(ANQP_REQ_EMERGENCY_NAI, + "Emergency NAI", + get_anqp_elem(hapd, info_id) != NULL, qi); + break; default: - wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", - info_id); + if (!get_anqp_elem(hapd, info_id)) { + wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", + info_id); + break; + } + if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) { + wpa_printf(MSG_DEBUG, + "ANQP: No more room for extra requests - ignore Info Id %u", + info_id); + break; + } + wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id); + qi->extra_req[qi->num_extra_req] = info_id; + qi->num_extra_req++; break; } } @@ -817,7 +1010,7 @@ static void rx_anqp_query_list(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list", (unsigned int) (end - pos) / 2); - while (pos + 2 <= end) { + while (end - pos >= 2) { rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi); pos += 2; } @@ -906,7 +1099,7 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd, u32 oui; u8 subtype; - if (pos + 4 > end) { + if (end - pos < 4) { wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " "Query element"); return; @@ -942,7 +1135,7 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd, } pos++; - if (pos + 1 >= end) + if (end - pos <= 1) return; subtype = *pos++; @@ -973,14 +1166,16 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd, static void gas_serv_req_local_processing(struct hostapd_data *hapd, const u8 *sa, u8 dialog_token, - struct anqp_query_info *qi, int prot) + struct anqp_query_info *qi, int prot, + int std_addr3) { struct wpabuf *buf, *tx_buf; buf = gas_serv_build_gas_resp_payload(hapd, qi->request, qi->home_realm_query, qi->home_realm_query_len, - qi->icon_name, qi->icon_name_len); + qi->icon_name, qi->icon_name_len, + qi->extra_req, qi->num_extra_req); wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", buf); if (!buf) @@ -1033,15 +1228,22 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd, return; if (prot) convert_to_protected_dual(tx_buf); - hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, - wpabuf_head(tx_buf), wpabuf_len(tx_buf)); + if (std_addr3) + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); + else + hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); wpabuf_free(tx_buf); } static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, const u8 *sa, - const u8 *data, size_t len, int prot) + const u8 *data, size_t len, int prot, + int std_addr3) { const u8 *pos = data; const u8 *end = data + len; @@ -1069,12 +1271,12 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, adv_proto = pos++; slen = *pos++; - next = pos + slen; - if (next > end || slen < 2) { + if (slen > end - pos || slen < 2) { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Invalid IE in GAS Initial Request"); return; } + next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { @@ -1093,19 +1295,26 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, wpabuf_put_le16(buf, 0); /* Query Response Length */ if (prot) convert_to_protected_dual(buf); - hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, - wpabuf_head(buf), wpabuf_len(buf)); + if (std_addr3) + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(buf), + wpabuf_len(buf)); + else + hostapd_drv_send_action_addr3_ap(hapd, + hapd->iface->freq, 0, + sa, wpabuf_head(buf), + wpabuf_len(buf)); wpabuf_free(buf); return; } pos = next; /* Query Request */ - if (pos + 2 > end) + if (end - pos < 2) return; slen = WPA_GET_LE16(pos); pos += 2; - if (pos + slen > end) + if (slen > end - pos) return; end = pos + slen; @@ -1113,7 +1322,7 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, while (pos < end) { u16 info_id, elen; - if (pos + 4 > end) + if (end - pos < 4) return; info_id = WPA_GET_LE16(pos); @@ -1121,7 +1330,7 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, elen = WPA_GET_LE16(pos); pos += 2; - if (pos + elen > end) { + if (elen > end - pos) { wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request"); return; } @@ -1144,13 +1353,15 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, pos += elen; } - gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot); + gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot, + std_addr3); } static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, const u8 *sa, - const u8 *data, size_t len, int prot) + const u8 *data, size_t len, int prot, + int std_addr3) { struct gas_dialog_info *dialog; struct wpabuf *buf, *tx_buf; @@ -1226,8 +1437,14 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, send_resp: if (prot) convert_to_protected_dual(tx_buf); - hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, - wpabuf_head(tx_buf), wpabuf_len(tx_buf)); + if (std_addr3) + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); + else + hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); wpabuf_free(tx_buf); } @@ -1238,7 +1455,7 @@ static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len, struct hostapd_data *hapd = ctx; const struct ieee80211_mgmt *mgmt; const u8 *sa, *data; - int prot; + int prot, std_addr3; mgmt = (const struct ieee80211_mgmt *) buf; if (len < IEEE80211_HDRLEN + 2) @@ -1253,14 +1470,22 @@ static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len, */ prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL; sa = mgmt->sa; + if (hapd->conf->gas_address3 == 1) + std_addr3 = 1; + else if (hapd->conf->gas_address3 == 2) + std_addr3 = 0; + else + std_addr3 = is_broadcast_ether_addr(mgmt->bssid); len -= IEEE80211_HDRLEN + 1; data = buf + IEEE80211_HDRLEN + 1; switch (data[0]) { case WLAN_PA_GAS_INITIAL_REQ: - gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot); + gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot, + std_addr3); break; case WLAN_PA_GAS_COMEBACK_REQ: - gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot); + gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot, + std_addr3); break; } } diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h index 4ec3201967c00..9051e4f905135 100644 --- a/src/ap/gas_serv.h +++ b/src/ap/gas_serv.h @@ -9,10 +9,13 @@ #ifndef GAS_SERV_H #define GAS_SERV_H +/* First 16 ANQP InfoIDs can be included in the optimized bitmap */ #define ANQP_REQ_CAPABILITY_LIST \ (1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST)) #define ANQP_REQ_VENUE_NAME \ (1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST)) +#define ANQP_REQ_EMERGENCY_CALL_NUMBER \ + (1 << (ANQP_EMERGENCY_CALL_NUMBER - ANQP_QUERY_LIST)) #define ANQP_REQ_NETWORK_AUTH_TYPE \ (1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST)) #define ANQP_REQ_ROAMING_CONSORTIUM \ @@ -23,8 +26,24 @@ (1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST)) #define ANQP_REQ_3GPP_CELLULAR_NETWORK \ (1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST)) +#define ANQP_REQ_AP_GEOSPATIAL_LOCATION \ + (1 << (ANQP_AP_GEOSPATIAL_LOCATION - ANQP_QUERY_LIST)) +#define ANQP_REQ_AP_CIVIC_LOCATION \ + (1 << (ANQP_AP_CIVIC_LOCATION - ANQP_QUERY_LIST)) +#define ANQP_REQ_AP_LOCATION_PUBLIC_URI \ + (1 << (ANQP_AP_LOCATION_PUBLIC_URI - ANQP_QUERY_LIST)) #define ANQP_REQ_DOMAIN_NAME \ (1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST)) +#define ANQP_REQ_EMERGENCY_ALERT_URI \ + (1 << (ANQP_EMERGENCY_ALERT_URI - ANQP_QUERY_LIST)) +#define ANQP_REQ_TDLS_CAPABILITY \ + (1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST)) +#define ANQP_REQ_EMERGENCY_NAI \ + (1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST)) +/* + * First 16 Hotspot 2.0 vendor specific ANQP-elements can be included in the + * optimized bitmap. + */ #define ANQP_REQ_HS_CAPABILITY_LIST \ (0x10000 << HS20_STYPE_CAPABILITY_LIST) #define ANQP_REQ_OPERATOR_FRIENDLY_NAME \ diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index c09c17a446963..9fafc7f457bb0 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -12,6 +12,7 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" +#include "common/hw_features_common.h" #include "radius/radius_client.h" #include "radius/radius_das.h" #include "eap_server/tncs.h" @@ -42,6 +43,8 @@ #include "x_snoop.h" #include "dhcp_snoop.h" #include "ndisc_snoop.h" +#include "neighbor_db.h" +#include "rrm.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); @@ -203,10 +206,12 @@ int hostapd_reload_config(struct hostapd_iface *iface) static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, - char *ifname) + const char *ifname) { int i; + if (!ifname) + return; for (i = 0; i < NUM_WEP_KEYS; i++) { if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, 0, NULL, 0, NULL, 0)) { @@ -334,6 +339,8 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) wpabuf_free(hapd->mesh_pending_auth); hapd->mesh_pending_auth = NULL; #endif /* CONFIG_MESH */ + + hostapd_clean_rrm(hapd); } @@ -367,7 +374,7 @@ static void sta_track_deinit(struct hostapd_iface *iface) list))) { dl_list_del(&info->list); iface->num_sta_seen--; - os_free(info); + sta_track_del(info); } } @@ -511,6 +518,9 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) if (hostapd_drv_none(hapd)) return 0; + if (iface->conf->use_driver_iface_addr) + return 0; + /* Generate BSSID mask that is large enough to cover the BSSIDs. */ /* Determine the bits necessary to cover the number of BSSIDs. */ @@ -520,7 +530,7 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) /* Determine the bits necessary to any configured BSSIDs, if they are higher than the number of BSSIDs. */ for (j = 0; j < iface->conf->num_bss; j++) { - if (hostapd_mac_comp_empty(iface->conf->bss[j]->bssid) == 0) { + if (is_zero_ether_addr(iface->conf->bss[j]->bssid)) { if (j) auto_addr++; continue; @@ -672,7 +682,7 @@ static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd, if (attr->acct_session_id) { num_attr++; - if (attr->acct_session_id_len != 17) { + if (attr->acct_session_id_len != 16) { wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Session-Id cannot match"); return NULL; @@ -682,10 +692,9 @@ static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd, for (sta = hapd->sta_list; sta; sta = sta->next) { if (!sta->radius_das_match) continue; - os_snprintf(buf, sizeof(buf), "%08X-%08X", - sta->acct_session_id_hi, - sta->acct_session_id_lo); - if (os_memcmp(attr->acct_session_id, buf, 17) != 0) + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) sta->acct_session_id); + if (os_memcmp(attr->acct_session_id, buf, 16) != 0) sta->radius_das_match = 0; else count++; @@ -701,7 +710,7 @@ static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd, if (attr->acct_multi_session_id) { num_attr++; - if (attr->acct_multi_session_id_len != 17) { + if (attr->acct_multi_session_id_len != 16) { wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Multi-Session-Id cannot match"); return NULL; @@ -712,14 +721,14 @@ static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd, if (!sta->radius_das_match) continue; if (!sta->eapol_sm || - !sta->eapol_sm->acct_multi_session_id_hi) { + !sta->eapol_sm->acct_multi_session_id) { sta->radius_das_match = 0; continue; } - os_snprintf(buf, sizeof(buf), "%08X+%08X", - sta->eapol_sm->acct_multi_session_id_hi, - sta->eapol_sm->acct_multi_session_id_lo); - if (os_memcmp(attr->acct_multi_session_id, buf, 17) != + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) + sta->eapol_sm->acct_multi_session_id); + if (os_memcmp(attr->acct_multi_session_id, buf, 16) != 0) sta->radius_das_match = 0; else @@ -905,12 +914,9 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) hapd->started = 1; if (!first || first == -1) { - if (hostapd_mac_comp_empty(conf->bssid) == 0) { - /* Allocate the next available BSSID. */ - do { - inc_byte_array(hapd->own_addr, ETH_ALEN); - } while (mac_in_conf(hapd->iconf, hapd->own_addr)); - } else { + u8 *addr = hapd->own_addr; + + if (!is_zero_ether_addr(conf->bssid)) { /* Allocate the configured BSSID. */ os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN); @@ -922,11 +928,18 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) "the radio", conf->iface); return -1; } + } else if (hapd->iconf->use_driver_iface_addr) { + addr = NULL; + } else { + /* Allocate the next available BSSID. */ + do { + inc_byte_array(hapd->own_addr, ETH_ALEN); + } while (mac_in_conf(hapd->iconf, hapd->own_addr)); } hapd->interface_added = 1; if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS, - conf->iface, hapd->own_addr, hapd, + conf->iface, addr, hapd, &hapd->drv_priv, force_ifname, if_addr, conf->bridge[0] ? conf->bridge : NULL, first == -1)) { @@ -935,11 +948,19 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) hapd->interface_added = 0; return -1; } + + if (!addr) + os_memcpy(hapd->own_addr, if_addr, ETH_ALEN); } if (conf->wmm_enabled < 0) conf->wmm_enabled = hapd->iconf->ieee80211n; +#ifdef CONFIG_IEEE80211R + if (is_zero_ether_addr(conf->r1_key_holder)) + os_memcpy(conf->r1_key_holder, hapd->own_addr, ETH_ALEN); +#endif /* CONFIG_IEEE80211R */ + #ifdef CONFIG_MESH if (hapd->iface->mconf == NULL) flush_old_stations = 0; @@ -1022,6 +1043,8 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) das_conf.time_window = conf->radius_das_time_window; das_conf.require_event_timestamp = conf->radius_das_require_event_timestamp; + das_conf.require_message_authenticator = + conf->radius_das_require_message_authenticator; das_conf.ctx = hapd; das_conf.disconnect = hostapd_das_disconnect; hapd->radius_das = radius_das_init(&das_conf); @@ -1509,15 +1532,128 @@ void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, #endif /* CONFIG_FST */ -/** - * hostapd_setup_interface_complete - Complete interface setup - * - * This function is called when previous steps in the interface setup has been - * completed. This can also start operations, e.g., DFS, that will require - * additional processing before interface is ready to be enabled. Such - * operations will call this function from eloop callbacks when finished. - */ -int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) +#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; + int center_freq1 = 0, center_freq2 = 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 */ + + ieee80211_freq_to_channel_ext(hapd->iface->freq, + hapd->iconf->secondary_channel, + hapd->iconf->vht_oper_chwidth, + &op_class, &channel); + width = hostapd_get_nr_chan_width(hapd, ht, vht); + if (vht) { + center_freq1 = ieee80211_chan_to_freq( + NULL, op_class, + hapd->iconf->vht_oper_centr_freq_seg0_idx); + if (width == NR_CHAN_WIDTH_80P80) + center_freq2 = ieee80211_chan_to_freq( + NULL, op_class, + hapd->iconf->vht_oper_centr_freq_seg1_idx); + } else if (ht) { + center_freq1 = hapd->iface->freq + + 10 * hapd->iconf->secondary_channel; + } + + 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); + wpabuf_put_u8(nr, center_freq2); + + hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci, + hapd->iconf->civic); + + wpabuf_free(nr); +#endif /* NEED_AP_MLME */ +} + + +static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, + int err) { struct hostapd_data *hapd = iface->bss[0]; size_t j; @@ -1633,7 +1769,7 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) } while (j-- > 0); goto fail; } - if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) + if (is_zero_ether_addr(hapd->conf->bssid)) prev_addr = hapd->own_addr; } hapd = iface->bss[0]; @@ -1641,7 +1777,6 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) hostapd_tx_queue_params(iface); ap_list_init(iface); - dl_list_init(&iface->sta_seen); hostapd_set_acl(hapd); @@ -1701,6 +1836,9 @@ dfs_offload: if (iface->interfaces && iface->interfaces->terminate_on_error > 0) iface->interfaces->terminate_on_error--; + for (j = 0; j < iface->num_bss; j++) + hostapd_set_own_neighbor_report(iface->bss[j]); + return 0; fail: @@ -1720,6 +1858,89 @@ fail: /** + * hostapd_setup_interface_complete - Complete interface setup + * + * This function is called when previous steps in the interface setup has been + * completed. This can also start operations, e.g., DFS, that will require + * additional processing before interface is ready to be enabled. Such + * operations will call this function from eloop callbacks when finished. + */ +int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) +{ + struct hapd_interfaces *interfaces = iface->interfaces; + struct hostapd_data *hapd = iface->bss[0]; + unsigned int i; + int not_ready_in_sync_ifaces = 0; + + if (!iface->need_to_start_in_sync) + return hostapd_setup_interface_complete_sync(iface, err); + + if (err) { + wpa_printf(MSG_ERROR, "Interface initialization failed"); + hostapd_set_state(iface, HAPD_IFACE_DISABLED); + iface->need_to_start_in_sync = 0; + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); + if (interfaces && interfaces->terminate_on_error) + eloop_terminate(); + return -1; + } + + if (iface->ready_to_start_in_sync) { + /* Already in ready and waiting. should never happpen */ + return 0; + } + + for (i = 0; i < interfaces->count; i++) { + if (interfaces->iface[i]->need_to_start_in_sync && + !interfaces->iface[i]->ready_to_start_in_sync) + not_ready_in_sync_ifaces++; + } + + /* + * Check if this is the last interface, if yes then start all the other + * waiting interfaces. If not, add this interface to the waiting list. + */ + if (not_ready_in_sync_ifaces > 1 && iface->state == HAPD_IFACE_DFS) { + /* + * If this interface went through CAC, do not synchronize, just + * start immediately. + */ + iface->need_to_start_in_sync = 0; + wpa_printf(MSG_INFO, + "%s: Finished CAC - bypass sync and start interface", + iface->bss[0]->conf->iface); + return hostapd_setup_interface_complete_sync(iface, err); + } + + if (not_ready_in_sync_ifaces > 1) { + /* need to wait as there are other interfaces still coming up */ + iface->ready_to_start_in_sync = 1; + wpa_printf(MSG_INFO, + "%s: Interface waiting to sync with other interfaces", + iface->bss[0]->conf->iface); + return 0; + } + + wpa_printf(MSG_INFO, + "%s: Last interface to sync - starting all interfaces", + iface->bss[0]->conf->iface); + iface->need_to_start_in_sync = 0; + hostapd_setup_interface_complete_sync(iface, err); + for (i = 0; i < interfaces->count; i++) { + if (interfaces->iface[i]->need_to_start_in_sync && + interfaces->iface[i]->ready_to_start_in_sync) { + hostapd_setup_interface_complete_sync( + interfaces->iface[i], 0); + /* Only once the interfaces are sync started */ + interfaces->iface[i]->need_to_start_in_sync = 0; + } + } + + return 0; +} + + +/** * hostapd_setup_interface - Setup of an interface * @iface: Pointer to interface data. * Returns: 0 on success, -1 on failure @@ -1778,6 +1999,8 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, hapd->iface = hapd_iface; hapd->driver = hapd->iconf->driver; hapd->ctrl_sock = -1; + dl_list_init(&hapd->ctrl_dst); + dl_list_init(&hapd->nr_db); return hapd; } @@ -1785,6 +2008,8 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, static void hostapd_bss_deinit(struct hostapd_data *hapd) { + if (!hapd) + return; wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__, hapd->conf->iface); hostapd_bss_deinit_no_free(hapd); @@ -1819,8 +2044,11 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) } #endif /* CONFIG_FST */ - for (j = iface->num_bss - 1; j >= 0; j--) + for (j = iface->num_bss - 1; j >= 0; j--) { + if (!iface->bss) + break; hostapd_bss_deinit(iface->bss[j]); + } } @@ -1829,6 +2057,8 @@ void hostapd_interface_free(struct hostapd_iface *iface) size_t j; wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); for (j = 0; j < iface->num_bss; j++) { + if (!iface->bss) + break; wpa_printf(MSG_DEBUG, "%s: free hapd %p", __func__, iface->bss[j]); os_free(iface->bss[j]); @@ -1837,6 +2067,20 @@ void hostapd_interface_free(struct hostapd_iface *iface) } +struct hostapd_iface * hostapd_alloc_iface(void) +{ + struct hostapd_iface *hapd_iface; + + hapd_iface = os_zalloc(sizeof(*hapd_iface)); + if (!hapd_iface) + return NULL; + + dl_list_init(&hapd_iface->sta_seen); + + return hapd_iface; +} + + /** * hostapd_init - Allocate and initialize per-interface data * @config_file: Path to the configuration file @@ -1854,7 +2098,7 @@ struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces, struct hostapd_data *hapd; size_t i; - hapd_iface = os_zalloc(sizeof(*hapd_iface)); + hapd_iface = hostapd_alloc_iface(); if (hapd_iface == NULL) goto fail; @@ -2190,7 +2434,7 @@ hostapd_iface_alloc(struct hapd_interfaces *interfaces) return NULL; interfaces->iface = iface; hapd_iface = interfaces->iface[interfaces->count] = - os_zalloc(sizeof(*hapd_iface)); + hostapd_alloc_iface(); if (hapd_iface == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " "the interface", __func__); @@ -2557,6 +2801,7 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, } hostapd_prune_associations(hapd, sta->addr); + ap_sta_clear_disconnect_timeouts(hapd, sta); /* IEEE 802.11F (IAPP) */ if (hapd->conf->ieee802_11f) @@ -2590,9 +2835,10 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { - wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " - "for " MACSTR " (%d seconds - ap_max_inactivity)", - __func__, MAC2STR(sta->addr), + wpa_printf(MSG_DEBUG, + "%s: %s: reschedule ap_handle_timer timeout for " + MACSTR " (%d seconds - ap_max_inactivity)", + hapd->conf->iface, __func__, MAC2STR(sta->addr), hapd->conf->ap_max_inactivity); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, @@ -2627,12 +2873,23 @@ const char * hostapd_state_text(enum hostapd_iface_state s) void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s) { wpa_printf(MSG_INFO, "%s: interface state %s->%s", - iface->conf->bss[0]->iface, hostapd_state_text(iface->state), - hostapd_state_text(s)); + iface->conf ? iface->conf->bss[0]->iface : "N/A", + hostapd_state_text(iface->state), hostapd_state_text(s)); iface->state = s; } +int hostapd_csa_in_progress(struct hostapd_iface *iface) +{ + unsigned int i; + + for (i = 0; i < iface->num_bss; i++) + if (iface->bss[i]->csa_in_progress) + return 1; + return 0; +} + + #ifdef NEED_AP_MLME static void free_beacon_data(struct beacon_data *beacon) @@ -2744,9 +3001,9 @@ free_ap_params: /* - * TODO: This flow currently supports only changing frequency within the - * same hw_mode. Any other changes to MAC parameters or provided settings (even - * width) are not supported. + * TODO: This flow currently supports only changing channel and width within + * the same hw_mode. Any other changes to MAC parameters or provided settings + * are not supported. */ static int hostapd_change_config_freq(struct hostapd_data *hapd, struct hostapd_config *conf, @@ -2765,15 +3022,44 @@ static int hostapd_change_config_freq(struct hostapd_data *hapd, return -1; /* if a pointer to old_params is provided we save previous state */ - if (old_params) { - old_params->channel = conf->channel; - old_params->ht_enabled = conf->ieee80211n; - old_params->sec_channel_offset = conf->secondary_channel; + if (old_params && + hostapd_set_freq_params(old_params, conf->hw_mode, + hostapd_hw_get_freq(hapd, conf->channel), + conf->channel, conf->ieee80211n, + conf->ieee80211ac, + conf->secondary_channel, + conf->vht_oper_chwidth, + conf->vht_oper_centr_freq_seg0_idx, + conf->vht_oper_centr_freq_seg1_idx, + conf->vht_capab)) + return -1; + + switch (params->bandwidth) { + case 0: + case 20: + case 40: + conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; + break; + case 80: + if (params->center_freq2) + conf->vht_oper_chwidth = VHT_CHANWIDTH_80P80MHZ; + else + conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ; + break; + case 160: + conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ; + break; + default: + return -1; } conf->channel = channel; conf->ieee80211n = params->ht_enabled; conf->secondary_channel = params->sec_channel_offset; + ieee80211_freq_to_chan(params->center_freq1, + &conf->vht_oper_centr_freq_seg0_idx); + ieee80211_freq_to_chan(params->center_freq2, + &conf->vht_oper_centr_freq_seg1_idx); /* TODO: maybe call here hostapd_config_check here? */ @@ -2787,11 +3073,43 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd, struct hostapd_iface *iface = hapd->iface; struct hostapd_freq_params old_freq; int ret; + u8 chan, vht_bandwidth; os_memset(&old_freq, 0, sizeof(old_freq)); if (!iface || !iface->freq || hapd->csa_in_progress) return -1; + switch (settings->freq_params.bandwidth) { + case 80: + if (settings->freq_params.center_freq2) + vht_bandwidth = VHT_CHANWIDTH_80P80MHZ; + else + vht_bandwidth = VHT_CHANWIDTH_80MHZ; + break; + case 160: + vht_bandwidth = VHT_CHANWIDTH_160MHZ; + break; + default: + vht_bandwidth = VHT_CHANWIDTH_USE_HT; + break; + } + + if (ieee80211_freq_to_channel_ext( + settings->freq_params.freq, + settings->freq_params.sec_channel_offset, + vht_bandwidth, + &hapd->iface->cs_oper_class, + &chan) == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_DEBUG, + "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d)", + settings->freq_params.freq, + settings->freq_params.sec_channel_offset, + settings->freq_params.vht_enabled); + return -1; + } + + settings->freq_params.channel = chan; + ret = hostapd_change_config_freq(iface->bss[0], iface->conf, &settings->freq_params, &old_freq); @@ -2818,8 +3136,10 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd, return ret; } - settings->counter_offset_beacon = hapd->cs_c_off_beacon; - settings->counter_offset_presp = hapd->cs_c_off_proberesp; + settings->counter_offset_beacon[0] = hapd->cs_c_off_beacon; + settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp; + settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon; + settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp; return 0; } @@ -2833,6 +3153,8 @@ void hostapd_cleanup_cs_params(struct hostapd_data *hapd) hapd->cs_c_off_beacon = 0; hapd->cs_c_off_proberesp = 0; hapd->csa_in_progress = 0; + hapd->cs_c_off_ecsa_beacon = 0; + hapd->cs_c_off_ecsa_proberesp = 0; } @@ -2920,6 +3242,8 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface, hostapd_enable_iface(iface); } +#endif /* NEED_AP_MLME */ + struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, const char *ifname) @@ -2940,8 +3264,6 @@ struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, return NULL; } -#endif /* NEED_AP_MLME */ - void hostapd_periodic_iface(struct hostapd_iface *iface) { diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index dcf51f00f78d3..dec46f692206c 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -41,7 +41,7 @@ struct hapd_interfaces { size_t count; int global_ctrl_sock; - struct wpa_ctrl_dst *global_ctrl_dst; + struct dl_list global_ctrl_dst; char *global_iface_path; char *global_iface_name; #ifndef CONFIG_NATIVE_WINDOWS @@ -53,6 +53,7 @@ struct hapd_interfaces { #ifndef CONFIG_NO_VLAN struct dynamic_iface *vlan_priv; #endif /* CONFIG_NO_VLAN */ + int eloop_initialized; }; enum hostapd_chan_status { @@ -99,6 +100,16 @@ struct wps_stat { u8 peer_addr[ETH_ALEN]; }; +struct hostapd_neighbor_entry { + struct dl_list list; + u8 bssid[ETH_ALEN]; + struct wpa_ssid_value ssid; + struct wpabuf *nr; + struct wpabuf *lci; + struct wpabuf *civic; + /* LCI update time */ + struct os_time lci_date; +}; /** * struct hostapd_data - hostapd per-BSS data structure @@ -138,7 +149,7 @@ struct hostapd_data { void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */ struct radius_client_data *radius; - u32 acct_session_id_hi, acct_session_id_lo; + u64 acct_session_id; struct radius_das_data *radius_das; struct iapp_data *iapp; @@ -155,7 +166,7 @@ struct hostapd_data { int tkip_countermeasures; int ctrl_sock; - struct wpa_ctrl_dst *ctrl_dst; + struct dl_list ctrl_dst; void *ssl_ctx; void *eap_sim_db_priv; @@ -228,6 +239,8 @@ struct hostapd_data { unsigned int cs_c_off_beacon; unsigned int cs_c_off_proberesp; int csa_in_progress; + unsigned int cs_c_off_ecsa_beacon; + unsigned int cs_c_off_ecsa_proberesp; /* BSS Load */ unsigned int bss_load_update_timeout; @@ -256,9 +269,11 @@ struct hostapd_data { #ifdef CONFIG_MESH int num_plinks; int max_plinks; - void (*mesh_sta_free_cb)(struct sta_info *sta); + void (*mesh_sta_free_cb)(struct hostapd_data *hapd, + struct sta_info *sta); struct wpabuf *mesh_pending_auth; struct os_reltime mesh_pending_auth_time; + u8 mesh_required_peer[ETH_ALEN]; #endif /* CONFIG_MESH */ #ifdef CONFIG_SQLITE @@ -278,6 +293,17 @@ struct hostapd_data { struct l2_packet_data *l2_test; #endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_MBO + unsigned int mbo_assoc_disallow; +#endif /* CONFIG_MBO */ + + struct dl_list nr_db; + + u8 lci_req_token; + u8 range_req_token; + unsigned int lci_req_active:1; + unsigned int range_req_active:1; }; @@ -285,6 +311,9 @@ struct hostapd_sta_info { struct dl_list list; u8 addr[ETH_ALEN]; struct os_reltime last_seen; +#ifdef CONFIG_TAXONOMY + struct wpabuf *probe_ie_taxonomy; +#endif /* CONFIG_TAXONOMY */ }; /** @@ -327,6 +356,15 @@ struct hostapd_iface { */ unsigned int driver_ap_teardown:1; + /* + * When set, indicates that this interface is part of list of + * interfaces that need to be started together (synchronously). + */ + unsigned int need_to_start_in_sync:1; + + /* Ready to start but waiting for other interfaces to become ready. */ + unsigned int ready_to_start_in_sync:1; + int num_ap; /* number of entries in ap_list */ struct ap_info *ap_list; /* AP info list head */ struct ap_info *ap_hash[STA_HASH_SIZE]; @@ -402,6 +440,9 @@ struct hostapd_iface { u64 last_channel_time_busy; u8 channel_utilization; + /* eCSA IE will be added only if operating class is specified */ + u8 cs_oper_class; + unsigned int dfs_cac_ms; struct os_reltime dfs_cac_start; @@ -433,6 +474,7 @@ int hostapd_setup_interface(struct hostapd_iface *iface); int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err); void hostapd_interface_deinit(struct hostapd_iface *iface); void hostapd_interface_free(struct hostapd_iface *iface); +struct hostapd_iface * hostapd_alloc_iface(void); struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces, const char *config_file); struct hostapd_iface * @@ -449,6 +491,7 @@ int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf); void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator); void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s); const char * hostapd_state_text(enum hostapd_iface_state s); +int hostapd_csa_in_progress(struct hostapd_iface *iface); int hostapd_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings); void @@ -478,6 +521,11 @@ int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, int ssi_signal); void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, int offset, int width, int cf1, int cf2); +struct survey_results; +void hostapd_event_get_survey(struct hostapd_iface *iface, + struct survey_results *survey_results); +void hostapd_acs_channel_selected(struct hostapd_data *hapd, + struct acs_selected_channels *acs_res); const struct hostapd_eap_user * hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index fc8786dc311ca..16887acdfef49 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -329,6 +329,7 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) res = ieee80211n_allowed_ht40_channel_pair(iface); if (!res) { iface->conf->secondary_channel = 0; + res = 1; wpa_printf(MSG_INFO, "Fallback to 20 MHz"); } @@ -472,8 +473,9 @@ static int ieee80211n_check_40mhz(struct hostapd_iface *iface) struct wpa_driver_scan_params params; int ret; - if (!iface->conf->secondary_channel) - return 0; /* HT40 not used */ + /* Check that HT40 is used and PRI / SEC switch is allowed */ + if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch) + return 0; hostapd_set_state(iface, HAPD_IFACE_HT_SCAN); wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling " diff --git a/src/ap/iapp.c b/src/ap/iapp.c index 99aa04dc3dd91..2556da30c82f3 100644 --- a/src/ap/iapp.c +++ b/src/ap/iapp.c @@ -34,11 +34,7 @@ #include "utils/includes.h" #include <net/if.h> #include <sys/ioctl.h> -#ifdef USE_KERNEL_HEADERS -#include <linux/if_packet.h> -#else /* USE_KERNEL_HEADERS */ #include <netpacket/packet.h> -#endif /* USE_KERNEL_HEADERS */ #include "utils/common.h" #include "utils/eloop.h" @@ -385,6 +381,7 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) struct sockaddr_in *paddr, uaddr; struct iapp_data *iapp; struct ip_mreqn mreq; + int reuseaddr = 1; iapp = os_zalloc(sizeof(*iapp)); if (iapp == NULL) @@ -447,6 +444,18 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) os_memset(&uaddr, 0, sizeof(uaddr)); uaddr.sin_family = AF_INET; uaddr.sin_port = htons(IAPP_UDP_PORT); + + if (setsockopt(iapp->udp_sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, + sizeof(reuseaddr)) < 0) { + wpa_printf(MSG_INFO, + "iapp_init - setsockopt[UDP,SO_REUSEADDR]: %s", + strerror(errno)); + /* + * Ignore this and try to continue. This is fine for single + * BSS cases, but may fail if multiple BSSes enable IAPP. + */ + } + if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr, sizeof(uaddr)) < 0) { wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s", diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 7bb18c01d1a19..4e04169c73e69 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -42,6 +42,9 @@ #include "hw_features.h" #include "ieee802_11.h" #include "dfs.h" +#include "mbo_ap.h" +#include "rrm.h" +#include "taxonomy.h" u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) @@ -139,6 +142,7 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd) int capab = WLAN_CAPABILITY_ESS; int privacy; int dfs; + int i; /* Check if any of configured channels require DFS */ dfs = hostapd_is_dfs_required(hapd->iface); @@ -186,8 +190,12 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd) (hapd->iconf->spectrum_mgmt_required || dfs)) capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; - if (hapd->conf->radio_measurements) - capab |= IEEE80211_CAP_RRM; + for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) { + if (hapd->conf->radio_measurements[i]) { + capab |= IEEE80211_CAP_RRM; + break; + } + } return capab; } @@ -207,16 +215,17 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, if (!sta->challenge) { /* Generate a pseudo-random challenge */ u8 key[8]; - struct os_time now; - int r; + sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN); if (sta->challenge == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - os_get_time(&now); - r = os_random(); - os_memcpy(key, &now.sec, 4); - os_memcpy(key + 4, &r, 4); + if (os_get_random(key, sizeof(key)) < 0) { + os_free(sta->challenge); + sta->challenge = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + rc4_skip(key, sizeof(key), 0, sta->challenge, WLAN_AUTH_CHALLENGE_LEN); } @@ -250,19 +259,20 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_NO_RC4 */ -static void send_auth_reply(struct hostapd_data *hapd, - const u8 *dst, const u8 *bssid, - u16 auth_alg, u16 auth_transaction, u16 resp, - const u8 *ies, size_t ies_len) +static int send_auth_reply(struct hostapd_data *hapd, + const u8 *dst, const u8 *bssid, + u16 auth_alg, u16 auth_transaction, u16 resp, + const u8 *ies, size_t ies_len) { struct ieee80211_mgmt *reply; u8 *buf; size_t rlen; + int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE; rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len; buf = os_zalloc(rlen); if (buf == NULL) - return; + return -1; reply = (struct ieee80211_mgmt *) buf; reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, @@ -283,9 +293,13 @@ static void send_auth_reply(struct hostapd_data *hapd, MAC2STR(dst), auth_alg, auth_transaction, resp, (unsigned long) ies_len); if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0) - wpa_printf(MSG_INFO, "send_auth_reply: send"); + wpa_printf(MSG_INFO, "send_auth_reply: send failed"); + else + reply_res = WLAN_STATUS_SUCCESS; os_free(buf); + + return reply_res; } @@ -296,17 +310,25 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, { struct hostapd_data *hapd = ctx; struct sta_info *sta; + int reply_res; - send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction, - status, ies, ies_len); - - if (status != WLAN_STATUS_SUCCESS) - return; + reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, + auth_transaction, status, ies, ies_len); sta = ap_get_sta(hapd, dst); if (sta == NULL) return; + if (sta->added_unassoc && (reply_res != WLAN_STATUS_SUCCESS || + status != WLAN_STATUS_SUCCESS)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + return; + } + + if (status != WLAN_STATUS_SUCCESS) + return; + hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); sta->flags |= WLAN_STA_AUTH; @@ -369,18 +391,19 @@ static int auth_sae_send_commit(struct hostapd_data *hapd, const u8 *bssid, int update) { struct wpabuf *data; + int reply_res; data = auth_build_sae_commit(hapd, sta, update); if (data == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - send_auth_reply(hapd, sta->addr, bssid, - WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS, - wpabuf_head(data), wpabuf_len(data)); + reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1, + WLAN_STATUS_SUCCESS, wpabuf_head(data), + wpabuf_len(data)); wpabuf_free(data); - return WLAN_STATUS_SUCCESS; + return reply_res; } @@ -389,18 +412,19 @@ static int auth_sae_send_confirm(struct hostapd_data *hapd, const u8 *bssid) { struct wpabuf *data; + int reply_res; data = auth_build_sae_confirm(hapd, sta); if (data == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - send_auth_reply(hapd, sta->addr, bssid, - WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS, - wpabuf_head(data), wpabuf_len(data)); + reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2, + WLAN_STATUS_SUCCESS, wpabuf_head(data), + wpabuf_len(data)); wpabuf_free(data); - return WLAN_STATUS_SUCCESS; + return reply_res; } @@ -495,6 +519,9 @@ static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data) if (sae_check_big_sync(sta)) return; sta->sae->sync++; + wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR + " (sync=%d state=%d)", + MAC2STR(sta->addr), sta->sae->sync, sta->sae->state); switch (sta->sae->state) { case SAE_COMMITTED: @@ -537,6 +564,18 @@ static void sae_set_retransmit_timer(struct hostapd_data *hapd, } +void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) +{ + sta->flags |= WLAN_STA_AUTH; + sta->auth_alg = WLAN_AUTH_SAE; + mlme_authenticate_indication(hapd, sta); + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->sae->state = SAE_ACCEPTED; + wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, + sta->sae->pmk, sta->sae->pmkid); +} + + static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, const u8 *bssid, u8 auth_transaction) { @@ -580,7 +619,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, * message now to get alternating sequence of * Authentication frames between the AP and STA. * Confirm will be sent in - * Commited -> Confirmed/Accepted transition + * Committed -> Confirmed/Accepted transition * when receiving Confirm from STA. */ } @@ -659,13 +698,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, sae_set_retransmit_timer(hapd, sta); } else { - sta->flags |= WLAN_STA_AUTH; - sta->auth_alg = WLAN_AUTH_SAE; - mlme_authenticate_indication(hapd, sta); - wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); - sta->sae->state = SAE_ACCEPTED; - wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, - sta->sae->pmk); + sae_accept_sta(hapd, sta); } break; case SAE_ACCEPTED: @@ -674,6 +707,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, ") doing reauthentication", MAC2STR(sta->addr)); ap_free_sta(hapd, sta); + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); } else { if (sae_check_big_sync(sta)) return WLAN_STATUS_SUCCESS; @@ -694,23 +728,73 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, } +static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct sae_data *sae = sta->sae; + int i, *groups = hapd->conf->sae_groups; + + if (sae->state != SAE_COMMITTED) + return; + + wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group); + + for (i = 0; groups && groups[i] > 0; i++) { + if (sae->group == groups[i]) + break; + } + + if (!groups || groups[i] <= 0) { + wpa_printf(MSG_DEBUG, + "SAE: Previously selected group not found from the current configuration"); + return; + } + + for (;;) { + i++; + if (groups[i] <= 0) { + wpa_printf(MSG_DEBUG, + "SAE: No alternative group enabled"); + return; + } + + if (sae_set_group(sae, groups[i]) < 0) + continue; + + break; + } + wpa_printf(MSG_DEBUG, "SAE: Selected new group: %d", groups[i]); +} + + static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *mgmt, size_t len, u16 auth_transaction, u16 status_code) { - u16 resp = WLAN_STATUS_SUCCESS; + int resp = WLAN_STATUS_SUCCESS; struct wpabuf *data = NULL; if (!sta->sae) { - if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) - return; + if (auth_transaction != 1 || + status_code != WLAN_STATUS_SUCCESS) { + resp = -1; + goto remove_sta; + } sta->sae = os_zalloc(sizeof(*sta->sae)); - if (sta->sae == NULL) - return; + if (!sta->sae) { + resp = -1; + goto remove_sta; + } sta->sae->state = SAE_NOTHING; sta->sae->sync = 0; } + if (sta->mesh_sae_pmksa_caching) { + wpa_printf(MSG_DEBUG, + "SAE: Cancel use of mesh PMKSA caching because peer starts SAE authentication"); + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + sta->mesh_sae_pmksa_caching = 0; + } + if (auth_transaction == 1) { const u8 *token = NULL, *pos, *end; size_t token_len = 0; @@ -746,7 +830,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, if (sta->sae->tmp->anti_clogging_token == NULL) { wpa_printf(MSG_ERROR, "SAE: Failed to alloc for anti-clogging token"); - return; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto remove_sta; } /* @@ -756,10 +841,11 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, * Authentication frame, and the commit-scalar and * COMMIT-ELEMENT previously sent. */ - if (auth_sae_send_commit(hapd, sta, mgmt->bssid, 0)) { + resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0); + if (resp != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_ERROR, "SAE: Failed to send commit message"); - return; + goto remove_sta; } sta->sae->state = SAE_COMMITTED; sta->sae->sync = 0; @@ -767,8 +853,18 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, return; } + if ((hapd->conf->mesh & MESH_ENABLED) && + status_code == + WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && + sta->sae->tmp) { + wpa_printf(MSG_DEBUG, + "SAE: Peer did not accept our SAE group"); + sae_pick_next_group(hapd, sta); + goto remove_sta; + } + if (status_code != WLAN_STATUS_SUCCESS) - return; + goto remove_sta; resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, ((const u8 *) mgmt) + len - @@ -778,14 +874,15 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "SAE: Drop commit message from " MACSTR " due to reflection attack", MAC2STR(sta->addr)); - return; + goto remove_sta; } if (token && check_sae_token(hapd, sta->addr, token, token_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " "incorrect token from " MACSTR, MAC2STR(sta->addr)); - return; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto remove_sta; } if (resp != WLAN_STATUS_SUCCESS) @@ -810,7 +907,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, "SAE authentication (RX confirm, status=%u)", status_code); if (status_code != WLAN_STATUS_SUCCESS) - return; + goto remove_sta; if (sta->sae->state >= SAE_CONFIRMED || !(hapd->conf->mesh & MESH_ENABLED)) { if (sae_check_confirm(sta->sae, mgmt->u.auth.variable, @@ -827,7 +924,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, "unexpected SAE authentication transaction %u (status=%u)", auth_transaction, status_code); if (status_code != WLAN_STATUS_SUCCESS) - return; + goto remove_sta; resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; } @@ -838,6 +935,13 @@ reply: data ? wpabuf_head(data) : (u8 *) "", data ? wpabuf_len(data) : 0); } + +remove_sta: + if (sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || + status_code != WLAN_STATUS_SUCCESS)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } wpabuf_free(data); } @@ -882,11 +986,11 @@ static void handle_auth(struct hostapd_data *hapd, u16 auth_alg, auth_transaction, status_code; u16 resp = WLAN_STATUS_SUCCESS; struct sta_info *sta = NULL; - int res; + int res, reply_res; u16 fc; const u8 *challenge = NULL; u32 session_timeout, acct_interim_interval; - int vlan_id = 0; + struct vlan_description vlan_id; struct hostapd_sta_wpa_psk_short *psk = NULL; u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; size_t resp_ies_len = 0; @@ -894,6 +998,8 @@ static void handle_auth(struct hostapd_data *hapd, char *radius_cui = NULL; u16 seq_ctrl; + os_memset(&vlan_id, 0, sizeof(vlan_id)); + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)", (unsigned long) len); @@ -1067,13 +1173,22 @@ static void handle_auth(struct hostapd_data *hapd, seq_ctrl); return; } +#ifdef CONFIG_MESH + if ((hapd->conf->mesh & MESH_ENABLED) && + sta->plink_state == PLINK_BLOCKED) { + wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR + " is blocked - drop Authentication frame", + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_MESH */ } else { #ifdef CONFIG_MESH if (hapd->conf->mesh & MESH_ENABLED) { /* if the mesh peer is not available, we don't do auth. */ wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR - " not yet known - drop Authentiation frame", + " not yet known - drop Authentication frame", MAC2STR(mgmt->sa)); /* * Save a copy of the frame so that it can be processed @@ -1095,19 +1210,23 @@ static void handle_auth(struct hostapd_data *hapd, sta->last_seq_ctrl = seq_ctrl; sta->last_subtype = WLAN_FC_STYPE_AUTH; - if (vlan_id > 0) { - if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, "Invalid VLAN ID " - "%d received from RADIUS server", - vlan_id); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - sta->vlan_id = vlan_id; + if (vlan_id.notempty && + !hostapd_vlan_valid(hapd->conf->vlan, &vlan_id)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); + HOSTAPD_LEVEL_INFO, + "Invalid VLAN %d%s received from RADIUS server", + vlan_id.untagged, + vlan_id.tagged[0] ? "+" : ""); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; } + if (ap_sta_set_vlan(hapd, sta, &vlan_id) < 0) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + if (sta->vlan_id) + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); hostapd_free_psk_list(sta->psk); if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { @@ -1132,6 +1251,46 @@ static void handle_auth(struct hostapd_data *hapd, else ap_sta_no_session_timeout(hapd, sta); + /* + * If the driver supports full AP client state, add a station to the + * driver before sending authentication reply to make sure the driver + * has resources, and not to go through the entire authentication and + * association handshake, and fail it at the end. + * + * If this is not the first transaction, in a multi-step authentication + * algorithm, the station already exists in the driver + * (sta->added_unassoc = 1) so skip it. + * + * In mesh mode, the station was already added to the driver when the + * NEW_PEER_CANDIDATE event is received. + */ + if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && + !(hapd->conf->mesh & MESH_ENABLED) && + !(sta->added_unassoc)) { + /* + * If a station that is already associated to the AP, is trying + * to authenticate again, remove the STA entry, in order to make + * sure the STA PS state gets cleared and configuration gets + * updated. To handle this, station's added_unassoc flag is + * cleared once the station has completed association. + */ + hostapd_drv_sta_remove(hapd, sta->addr); + sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH | + WLAN_STA_AUTHORIZED); + + if (hostapd_sta_add(hapd, sta->addr, 0, 0, NULL, 0, 0, + NULL, NULL, sta->flags, 0, 0, 0, 0)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_NOTICE, + "Could not add STA to kernel driver"); + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } + + sta->added_unassoc = 1; + } + switch (auth_alg) { case WLAN_AUTH_OPEN: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -1205,12 +1364,19 @@ static void handle_auth(struct hostapd_data *hapd, os_free(radius_cui); hostapd_free_psk_list(psk); - send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, - auth_transaction + 1, resp, resp_ies, resp_ies_len); + reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, + auth_transaction + 1, resp, resp_ies, + resp_ies_len); + + if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || + reply_res != WLAN_STATUS_SUCCESS)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } } -static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) +int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) { int i, j = 32, aid; @@ -1220,6 +1386,9 @@ static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) return 0; } + if (TEST_FAIL()) + return -1; + for (i = 0; i < AID_WORDS; i++) { if (hapd->sta_aid[i] == (u32) -1) continue; @@ -1327,6 +1496,9 @@ static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_INTERWORKING */ + if (ext_capab_ie_len > 0) + sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2)); + return WLAN_STATUS_SUCCESS; } @@ -1617,6 +1789,27 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, sta->mb_ies = NULL; #endif /* CONFIG_FST */ +#ifdef CONFIG_MBO + mbo_ap_check_sta_assoc(hapd, sta, &elems); + + if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) && + elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) && + hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + wpa_printf(MSG_INFO, + "MBO: Reject WPA2 association without PMF"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } +#endif /* CONFIG_MBO */ + + ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes, + elems.supp_op_classes_len); + + if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) && + elems.rrm_enabled && + elems.rrm_enabled_len >= sizeof(sta->rrm_enabled_capa)) + os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled, + sizeof(sta->rrm_enabled_capa)); + return WLAN_STATUS_SUCCESS; } @@ -1643,9 +1836,66 @@ static void send_deauth(struct hostapd_data *hapd, const u8 *addr, } -static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, - u16 status_code, int reassoc, const u8 *ies, - size_t ies_len) +static int add_associated_sta(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct ieee80211_ht_capabilities ht_cap; + struct ieee80211_vht_capabilities vht_cap; + + /* + * Remove the STA entry to ensure the STA PS state gets cleared and + * configuration gets updated. This is relevant for cases, such as + * FT-over-the-DS, where a station re-associates back to the same AP but + * skips the authentication flow, or if working with a driver that + * does not support full AP client state. + */ + if (!sta->added_unassoc) + hostapd_drv_sta_remove(hapd, sta->addr); + +#ifdef CONFIG_IEEE80211N + if (sta->flags & WLAN_STA_HT) + hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); +#endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + if (sta->flags & WLAN_STA_VHT) + hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); +#endif /* CONFIG_IEEE80211AC */ + + /* + * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags + * will be set when the ACK frame for the (Re)Association Response frame + * is processed (TX status driver event). + */ + if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, + sta->supported_rates, sta->supported_rates_len, + sta->listen_interval, + sta->flags & WLAN_STA_HT ? &ht_cap : NULL, + sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, + sta->flags | WLAN_STA_ASSOC, sta->qosinfo, + sta->vht_opmode, sta->p2p_ie ? 1 : 0, + sta->added_unassoc)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, + "Could not %s STA to kernel driver", + sta->added_unassoc ? "set" : "add"); + + if (sta->added_unassoc) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } + + return -1; + } + + sta->added_unassoc = 0; + + return 0; +} + + +static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, + u16 status_code, int reassoc, const u8 *ies, + size_t ies_len) { int send_len; u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; @@ -1695,7 +1945,23 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { - p = hostapd_eid_vht_capabilities(hapd, p); + u32 nsts = 0, sta_nsts; + + if (hapd->conf->use_sta_nsts && sta->vht_capabilities) { + struct ieee80211_vht_capabilities *capa; + + nsts = (hapd->iface->conf->vht_capab >> + VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; + capa = sta->vht_capabilities; + sta_nsts = (le_to_host32(capa->vht_capabilities_info) >> + VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; + + if (nsts < sta_nsts) + nsts = 0; + else + nsts = sta_nsts; + } + p = hostapd_eid_vht_capabilities(hapd, p, nsts); p = hostapd_eid_vht_operation(hapd, p); } #endif /* CONFIG_IEEE80211AC */ @@ -1734,7 +2000,7 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P - if (sta->p2p_ie) { + if (sta->p2p_ie && hapd->p2p_group) { struct wpabuf *p2p_resp_ie; enum p2p_status_code status; switch (status_code) { @@ -1763,11 +2029,25 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, p = hostapd_eid_p2p_manage(hapd, p); #endif /* CONFIG_P2P_MANAGER */ + p = hostapd_eid_mbo(hapd, p, buf + sizeof(buf) - p); + + if (hapd->conf->assocresp_elements && + (size_t) (buf + sizeof(buf) - p) >= + wpabuf_len(hapd->conf->assocresp_elements)) { + os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements), + wpabuf_len(hapd->conf->assocresp_elements)); + p += wpabuf_len(hapd->conf->assocresp_elements); + } + send_len += p - reply->u.assoc_resp.variable; - if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) + if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) { wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + return WLAN_STATUS_SUCCESS; } @@ -1776,7 +2056,7 @@ static void handle_assoc(struct hostapd_data *hapd, int reassoc) { u16 capab_info, listen_interval, seq_ctrl, fc; - u16 resp = WLAN_STATUS_SUCCESS; + u16 resp = WLAN_STATUS_SUCCESS, reply_res; const u8 *pos; int left, i; struct sta_info *sta; @@ -1843,6 +2123,12 @@ static void handle_assoc(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " "prior to authentication since it is using " "over-the-DS FT", MAC2STR(mgmt->sa)); + + /* + * Mark station as authenticated, to avoid adding station + * entry in the driver as associated and not authenticated + */ + sta->flags |= WLAN_STA_AUTH; } else #endif /* CONFIG_IEEE80211R */ if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { @@ -1886,6 +2172,19 @@ static void handle_assoc(struct hostapd_data *hapd, goto fail; } +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) { + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } +#endif /* CONFIG_MBO */ + + /* + * sta->capability is used in check_assoc_ies() for RRM enabled + * capability element. + */ + sta->capability = capab_info; + /* followed by SSID and Supported rates; and HT capabilities if 802.11n * is used */ resp = check_assoc_ies(hapd, sta, pos, left, reassoc); @@ -1899,7 +2198,6 @@ static void handle_assoc(struct hostapd_data *hapd, goto fail; } - sta->capability = capab_info; sta->listen_interval = listen_interval; if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) @@ -1969,8 +2267,44 @@ static void handle_assoc(struct hostapd_data *hapd, * remove the STA immediately. */ sta->timeout_next = STA_NULLFUNC; +#ifdef CONFIG_TAXONOMY + taxonomy_sta_info_assoc_req(hapd, sta, pos, left); +#endif /* CONFIG_TAXONOMY */ + fail: - send_assoc_resp(hapd, sta, resp, reassoc, pos, left); + /* + * In case of a successful response, add the station to the driver. + * Otherwise, the kernel may ignore Data frames before we process the + * ACK frame (TX status). In case of a failure, this station will be + * removed. + * + * Note that this is not compliant with the IEEE 802.11 standard that + * states that a non-AP station should transition into the + * authenticated/associated state only after the station acknowledges + * the (Re)Association Response frame. However, still do this as: + * + * 1. In case the station does not acknowledge the (Re)Association + * Response frame, it will be removed. + * 2. Data frames will be dropped in the kernel until the station is + * set into authorized state, and there are no significant known + * issues with processing other non-Data Class 3 frames during this + * window. + */ + if (resp == WLAN_STATUS_SUCCESS && add_associated_sta(hapd, sta)) + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + + reply_res = send_assoc_resp(hapd, sta, resp, reassoc, pos, left); + + /* + * Remove the station in case tranmission of a success response fails + * (the STA was added associated to the driver) or if the station was + * previously added unassociated. + */ + if ((reply_res != WLAN_STATUS_SUCCESS && + resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } } @@ -2007,11 +2341,12 @@ static void handle_disassoc(struct hostapd_data *hapd, /* Stop Accounting and IEEE 802.1X sessions, but leave the STA * authenticated. */ accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); if (sta->ipaddr) hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); ap_sta_ip6addr_del(hapd, sta); hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; if (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC) { @@ -2233,6 +2568,9 @@ static int handle_action(struct hostapd_data *hapd, return 1; } break; + case WLAN_ACTION_RADIO_MEASUREMENT: + hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len); + return 1; } hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -2240,8 +2578,9 @@ static int handle_action(struct hostapd_data *hapd, "handle_action - unknown action category %d or invalid " "frame", mgmt->u.action.category); - if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) && - !(mgmt->sa[0] & 0x01)) { + if (!is_multicast_ether_addr(mgmt->da) && + !(mgmt->u.action.category & 0x80) && + !is_multicast_ether_addr(mgmt->sa)) { struct ieee80211_mgmt *resp; /* @@ -2288,7 +2627,6 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, struct hostapd_frame_info *fi) { struct ieee80211_mgmt *mgmt; - int broadcast; u16 fc, stype; int ret = 0; @@ -2304,11 +2642,7 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, return 1; } - broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff && - mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff && - mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff; - - if (!broadcast && + if (!is_broadcast_ether_addr(mgmt->bssid) && #ifdef CONFIG_P2P /* Invitation responses can be sent with the peer MAC as BSSID */ !((hapd->conf->p2p & P2P_GROUP_OWNER) && @@ -2388,28 +2722,28 @@ static void handle_auth_cb(struct hostapd_data *hapd, u16 auth_alg, auth_transaction, status_code; struct sta_info *sta; + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", + MAC2STR(mgmt->da)); + return; + } + + auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + status_code = le_to_host16(mgmt->u.auth.status_code); + if (!ok) { hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "did not acknowledge authentication response"); - return; + goto fail; } if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", (unsigned long) len); - return; - } - - auth_alg = le_to_host16(mgmt->u.auth.auth_alg); - auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); - status_code = le_to_host16(mgmt->u.auth.status_code); - - sta = ap_get_sta(hapd, mgmt->da); - if (!sta) { - wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", - MAC2STR(mgmt->da)); - return; + goto fail; } if (status_code == WLAN_STATUS_SUCCESS && @@ -2418,6 +2752,15 @@ static void handle_auth_cb(struct hostapd_data *hapd, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "authenticated"); sta->flags |= WLAN_STA_AUTH; + if (sta->added_unassoc) + hostapd_set_sta_flags(hapd, sta); + return; + } + +fail: + if (status_code != WLAN_STATUS_SUCCESS && sta->added_unassoc) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; } } @@ -2453,15 +2796,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd, u16 status; struct sta_info *sta; int new_assoc = 1; - struct ieee80211_ht_capabilities ht_cap; - struct ieee80211_vht_capabilities vht_cap; - - if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : - sizeof(mgmt->u.assoc_resp))) { - wpa_printf(MSG_INFO, "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)", - reassoc, (unsigned long) len); - return; - } sta = ap_get_sta(hapd, mgmt->da); if (!sta) { @@ -2470,11 +2804,12 @@ static void handle_assoc_cb(struct hostapd_data *hapd, return; } - if (!ok) { - hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "did not acknowledge association response"); - sta->flags &= ~WLAN_STA_ASSOC_REQ_OK; + if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : + sizeof(mgmt->u.assoc_resp))) { + wpa_printf(MSG_INFO, + "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)", + reassoc, (unsigned long) len); + hostapd_drv_sta_remove(hapd, sta->addr); return; } @@ -2483,6 +2818,18 @@ static void handle_assoc_cb(struct hostapd_data *hapd, else status = le_to_host16(mgmt->u.assoc_resp.status_code); + if (!ok) { + hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "did not acknowledge association response"); + sta->flags &= ~WLAN_STA_ASSOC_REQ_OK; + /* The STA is added only in case of SUCCESS */ + if (status == WLAN_STATUS_SUCCESS) + hostapd_drv_sta_remove(hapd, sta->addr); + + return; + } + if (status != WLAN_STATUS_SUCCESS) return; @@ -2517,38 +2864,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd, sta->sa_query_timed_out = 0; #endif /* CONFIG_IEEE80211W */ - /* - * Remove the STA entry in order to make sure the STA PS state gets - * cleared and configuration gets updated in case of reassociation back - * to the same AP. - */ - hostapd_drv_sta_remove(hapd, sta->addr); - -#ifdef CONFIG_IEEE80211N - if (sta->flags & WLAN_STA_HT) - hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); -#endif /* CONFIG_IEEE80211N */ -#ifdef CONFIG_IEEE80211AC - if (sta->flags & WLAN_STA_VHT) - hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); -#endif /* CONFIG_IEEE80211AC */ - - if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, - sta->supported_rates, sta->supported_rates_len, - sta->listen_interval, - sta->flags & WLAN_STA_HT ? &ht_cap : NULL, - sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, - sta->flags, sta->qosinfo, sta->vht_opmode)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_NOTICE, - "Could not add STA to kernel driver"); - - ap_sta_disconnect(hapd, sta, sta->addr, - WLAN_REASON_DISASSOC_AP_BUSY); - - return; - } - if (sta->flags & WLAN_STA_WDS) { int ret; char ifname_wds[IFNAMSIZ + 1]; @@ -2580,8 +2895,26 @@ static void handle_assoc_cb(struct hostapd_data *hapd, else wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); hapd->new_assoc_sta_cb(hapd, sta, !new_assoc); - ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + + if (sta->pending_eapol_rx) { + struct os_reltime now, age; + + os_get_reltime(&now); + os_reltime_sub(&now, &sta->pending_eapol_rx->rx_time, &age); + if (age.sec == 0 && age.usec < 200000) { + wpa_printf(MSG_DEBUG, + "Process pending EAPOL frame that was received from " MACSTR " just before association notification", + MAC2STR(sta->addr)); + ieee802_1x_receive( + hapd, mgmt->da, + wpabuf_head(sta->pending_eapol_rx->buf), + wpabuf_len(sta->pending_eapol_rx->buf)); + } + wpabuf_free(sta->pending_eapol_rx->buf); + os_free(sta->pending_eapol_rx); + sta->pending_eapol_rx = NULL; + } } @@ -2590,7 +2923,7 @@ static void handle_deauth_cb(struct hostapd_data *hapd, size_t len, int ok) { struct sta_info *sta; - if (mgmt->da[0] & 0x01) + if (is_multicast_ether_addr(mgmt->da)) return; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { @@ -2614,7 +2947,7 @@ static void handle_disassoc_cb(struct hostapd_data *hapd, size_t len, int ok) { struct sta_info *sta; - if (mgmt->da[0] & 0x01) + if (is_multicast_ether_addr(mgmt->da)) return; sta = ap_get_sta(hapd, mgmt->da); if (!sta) { @@ -2670,7 +3003,7 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, handle_assoc_cb(hapd, mgmt, len, 1, ok); break; case WLAN_FC_STYPE_PROBE_RESP: - wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb"); + wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb ok=%d", ok); break; case WLAN_FC_STYPE_DEAUTH: wpa_printf(MSG_DEBUG, "mgmt::deauth cb"); @@ -2681,7 +3014,7 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, handle_disassoc_cb(hapd, mgmt, len, ok); break; case WLAN_FC_STYPE_ACTION: - wpa_printf(MSG_DEBUG, "mgmt::action cb"); + wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok); break; default: wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype); @@ -2779,6 +3112,8 @@ void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr) } if (sta == NULL) return; + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POLL_OK MACSTR, + MAC2STR(sta->addr)); if (!(sta->flags & WLAN_STA_PENDING_POLL)) return; @@ -2817,7 +3152,7 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA " MACSTR, MAC2STR(src)); - if (src[0] & 0x01) { + if (is_multicast_ether_addr(src)) { /* Broadcast bit set in SA?! Ignore the frame silently. */ return; } diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 44c1bff364ac4..0327dec2a2bc3 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -49,9 +49,13 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); -u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts); u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid); + int hostapd_ht_operation_update(struct hostapd_iface *iface); void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, const u8 *addr, const u8 *trans_id); @@ -61,6 +65,7 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd, void hostapd_get_vht_capab(struct hostapd_data *hapd, struct ieee80211_vht_capabilities *vht_cap, struct ieee80211_vht_capabilities *neg_vht_cap); +int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta); u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab); u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, @@ -97,6 +102,7 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta); #ifdef CONFIG_SAE void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta); +void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta); #else /* CONFIG_SAE */ static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta) @@ -104,4 +110,29 @@ static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd, } #endif /* CONFIG_SAE */ +#ifdef CONFIG_MBO + +u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len); + +u8 hostapd_mbo_ie_len(struct hostapd_data *hapd); + +#else /* CONFIG_MBO */ + +static inline u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, + size_t len) +{ + return eid; +} + +static inline u8 hostapd_mbo_ie_len(struct hostapd_data *hapd) +{ + return 0; +} + +#endif /* CONFIG_MBO */ + +void ap_copy_sta_supp_op_classes(struct sta_info *sta, + const u8 *supp_op_classes, + size_t supp_op_classes_len); + #endif /* IEEE802_11_H */ diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index 531a67da412c6..b8905373618db 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -15,7 +15,6 @@ #include "utils/common.h" #include "utils/eloop.h" -#include "crypto/sha1.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "hostapd.h" @@ -35,7 +34,7 @@ struct hostapd_cached_radius_acl { struct hostapd_cached_radius_acl *next; u32 session_timeout; u32 acct_interim_interval; - int vlan_id; + struct vlan_description vlan_id; struct hostapd_sta_wpa_psk_short *psk; char *identity; char *radius_cui; @@ -77,29 +76,20 @@ static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk, struct hostapd_sta_wpa_psk_short *src) { - struct hostapd_sta_wpa_psk_short **copy_to; - struct hostapd_sta_wpa_psk_short *copy_from; - - /* Copy PSK linked list */ - copy_to = psk; - copy_from = src; - while (copy_from && copy_to) { - *copy_to = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); - if (*copy_to == NULL) - break; - os_memcpy(*copy_to, copy_from, - sizeof(struct hostapd_sta_wpa_psk_short)); - copy_from = copy_from->next; - copy_to = &((*copy_to)->next); - } - if (copy_to) - *copy_to = NULL; + if (!psk) + return; + + if (src) + src->ref++; + + *psk = src; } static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, u32 *session_timeout, - u32 *acct_interim_interval, int *vlan_id, + u32 *acct_interim_interval, + struct vlan_description *vlan_id, struct hostapd_sta_wpa_psk_short **psk, char **identity, char **radius_cui) { @@ -165,7 +155,10 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, if (msg == NULL) return -1; - radius_msg_make_authenticator(msg, addr, ETH_ALEN); + if (radius_msg_make_authenticator(msg) < 0) { + wpa_printf(MSG_INFO, "Could not make Request Authenticator"); + goto fail; + } os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr)); if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf, @@ -213,6 +206,33 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, /** + * hostapd_check_acl - Check a specified STA against accept/deny ACLs + * @hapd: hostapd BSS data + * @addr: MAC address of the STA + * @vlan_id: Buffer for returning VLAN ID + * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING + */ +int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, + struct vlan_description *vlan_id) +{ + if (hostapd_maclist_found(hapd->conf->accept_mac, + hapd->conf->num_accept_mac, addr, vlan_id)) + return HOSTAPD_ACL_ACCEPT; + + if (hostapd_maclist_found(hapd->conf->deny_mac, + hapd->conf->num_deny_mac, addr, vlan_id)) + return HOSTAPD_ACL_REJECT; + + if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED) + return HOSTAPD_ACL_ACCEPT; + if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED) + return HOSTAPD_ACL_REJECT; + + return HOSTAPD_ACL_PENDING; +} + + +/** * hostapd_allowed_address - Check whether a specified STA can be authenticated * @hapd: hostapd BSS data * @addr: MAC address of the STA @@ -231,16 +251,19 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, */ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, u32 *session_timeout, - u32 *acct_interim_interval, int *vlan_id, + u32 *acct_interim_interval, + struct vlan_description *vlan_id, struct hostapd_sta_wpa_psk_short **psk, char **identity, char **radius_cui) { + int res; + if (session_timeout) *session_timeout = 0; if (acct_interim_interval) *acct_interim_interval = 0; if (vlan_id) - *vlan_id = 0; + os_memset(vlan_id, 0, sizeof(*vlan_id)); if (psk) *psk = NULL; if (identity) @@ -248,18 +271,9 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, if (radius_cui) *radius_cui = NULL; - if (hostapd_maclist_found(hapd->conf->accept_mac, - hapd->conf->num_accept_mac, addr, vlan_id)) - return HOSTAPD_ACL_ACCEPT; - - if (hostapd_maclist_found(hapd->conf->deny_mac, - hapd->conf->num_deny_mac, addr, vlan_id)) - return HOSTAPD_ACL_REJECT; - - if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED) - return HOSTAPD_ACL_ACCEPT; - if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED) - return HOSTAPD_ACL_REJECT; + res = hostapd_check_acl(hapd, addr, vlan_id); + if (res != HOSTAPD_ACL_PENDING) + return res; if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) { #ifdef CONFIG_NO_RADIUS @@ -268,10 +282,9 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, struct hostapd_acl_query_data *query; /* Check whether ACL cache has an entry for this station */ - int res = hostapd_acl_cache_get(hapd, addr, session_timeout, - acct_interim_interval, - vlan_id, psk, - identity, radius_cui); + res = hostapd_acl_cache_get(hapd, addr, session_timeout, + acct_interim_interval, vlan_id, psk, + identity, radius_cui); if (res == HOSTAPD_ACL_ACCEPT || res == HOSTAPD_ACL_ACCEPT_TIMEOUT) return res; @@ -419,7 +432,7 @@ static void decode_tunnel_passwords(struct hostapd_data *hapd, struct hostapd_cached_radius_acl *cache) { int passphraselen; - char *passphrase, *strpassphrase; + char *passphrase; size_t i; struct hostapd_sta_wpa_psk_short *psk; @@ -436,24 +449,42 @@ static void decode_tunnel_passwords(struct hostapd_data *hapd, */ if (passphrase == NULL) break; + + /* + * Passphase should be 8..63 chars (to be hashed with SSID) + * or 64 chars hex string (no separate hashing with SSID). + */ + + if (passphraselen < MIN_PASSPHRASE_LEN || + passphraselen > MAX_PASSPHRASE_LEN + 1) + goto free_pass; + /* * passphrase does not contain the NULL termination. * Add it here as pbkdf2_sha1() requires it. */ - strpassphrase = os_zalloc(passphraselen + 1); psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); - if (strpassphrase && psk) { - os_memcpy(strpassphrase, passphrase, passphraselen); - pbkdf2_sha1(strpassphrase, - hapd->conf->ssid.ssid, - hapd->conf->ssid.ssid_len, 4096, - psk->psk, PMK_LEN); + if (psk) { + if ((passphraselen == MAX_PASSPHRASE_LEN + 1) && + (hexstr2bin(passphrase, psk->psk, PMK_LEN) < 0)) { + hostapd_logger(hapd, cache->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_WARNING, + "invalid hex string (%d chars) in Tunnel-Password", + passphraselen); + goto skip; + } else if (passphraselen <= MAX_PASSPHRASE_LEN) { + os_memcpy(psk->passphrase, passphrase, + passphraselen); + psk->is_passphrase = 1; + } psk->next = cache->psk; cache->psk = psk; psk = NULL; } - os_free(strpassphrase); +skip: os_free(psk); +free_pass: os_free(passphrase); } } @@ -478,6 +509,7 @@ 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; @@ -535,7 +567,12 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, cache->acct_interim_interval = 0; } - cache->vlan_id = radius_msg_get_vlanid(msg); + 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); decode_tunnel_passwords(hapd, shared_secret, shared_secret_len, msg, req, cache); @@ -558,17 +595,18 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, !cache->psk) cache->accepted = HOSTAPD_ACL_REJECT; - if (cache->vlan_id && - !hostapd_vlan_id_valid(hapd->conf->vlan, cache->vlan_id)) { + if (cache->vlan_id.notempty && + !hostapd_vlan_valid(hapd->conf->vlan, &cache->vlan_id)) { hostapd_logger(hapd, query->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "Invalid VLAN ID %d received from RADIUS server", - cache->vlan_id); - cache->vlan_id = 0; + "Invalid VLAN %d%s received from RADIUS server", + cache->vlan_id.untagged, + cache->vlan_id.tagged[0] ? "+" : ""); + os_memset(&cache->vlan_id, 0, sizeof(cache->vlan_id)); } if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && - !cache->vlan_id) + !cache->vlan_id.notempty) cache->accepted = HOSTAPD_ACL_REJECT; } else cache->accepted = HOSTAPD_ACL_REJECT; @@ -640,6 +678,12 @@ void hostapd_acl_deinit(struct hostapd_data *hapd) void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk) { + if (psk && psk->ref) { + /* This will be freed when the last reference is dropped. */ + psk->ref--; + return; + } + while (psk) { struct hostapd_sta_wpa_psk_short *prev = psk; psk = psk->next; diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h index b66f244b3ebc4..71f53b9612faf 100644 --- a/src/ap/ieee802_11_auth.h +++ b/src/ap/ieee802_11_auth.h @@ -16,9 +16,12 @@ enum { HOSTAPD_ACL_ACCEPT_TIMEOUT = 3 }; +int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, + struct vlan_description *vlan_id); int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, u32 *session_timeout, - u32 *acct_interim_interval, int *vlan_id, + u32 *acct_interim_interval, + struct vlan_description *vlan_id, struct hostapd_sta_wpa_psk_short **psk, char **identity, char **radius_cui); int hostapd_acl_init(struct hostapd_data *hapd); diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c index 11fde2a263946..5eb1060a29657 100644 --- a/src/ap/ieee802_11_ht.c +++ b/src/ap/ieee802_11_ht.c @@ -108,6 +108,29 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) } +u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid) +{ + u8 sec_ch; + + if (!hapd->cs_freq_params.channel || + !hapd->cs_freq_params.sec_channel_offset) + return eid; + + if (hapd->cs_freq_params.sec_channel_offset == -1) + sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW; + else if (hapd->cs_freq_params.sec_channel_offset == 1) + sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE; + else + return eid; + + *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; + *eid++ = 1; + *eid++ = sec_ch; + + return eid; +} + + /* op_mode Set to 0 (HT pure) under the followign conditions diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index d462ac8bf9cde..259413bd12ffb 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -172,6 +172,8 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) case 0: /* Bits 0-7 */ if (hapd->iconf->obss_interval) *pos |= 0x01; /* Bit 0 - Coexistence management */ + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) + *pos |= 0x04; /* Bit 2 - Extended Channel Switching */ break; case 1: /* Bits 8-15 */ if (hapd->conf->proxy_arp) @@ -207,11 +209,21 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) if (hapd->conf->hs20) *pos |= 0x40; /* Bit 46 - WNM-Notification */ #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled) + *pos |= 0x40; /* Bit 46 - WNM-Notification */ +#endif /* CONFIG_MBO */ break; case 6: /* Bits 48-55 */ if (hapd->conf->ssid.utf8_ssid) *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ break; + case 8: /* Bits 64-71 */ + if (hapd->conf->ftm_responder) + *pos |= 0x40; /* Bit 70 - FTM responder */ + if (hapd->conf->ftm_initiator) + *pos |= 0x80; /* Bit 71 - FTM initiator */ + break; } } @@ -231,6 +243,9 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) len = 1; if (len < 7 && hapd->conf->ssid.utf8_ssid) len = 7; + if (len < 9 && + (hapd->conf->ftm_initiator || hapd->conf->ftm_responder)) + len = 9; #ifdef CONFIG_WNM if (len < 4) len = 4; @@ -239,6 +254,10 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) if (hapd->conf->hs20 && len < 6) len = 6; #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled && len < 6) + len = 6; +#endif /* CONFIG_MBO */ if (len < hapd->iface->extended_capa_len) len = hapd->iface->extended_capa_len; if (len == 0) @@ -506,3 +525,62 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) return pos; } + + +#ifdef CONFIG_MBO + +u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len) +{ + u8 mbo[6], *mbo_pos = mbo; + u8 *pos = eid; + + if (!hapd->conf->mbo_enabled) + return eid; + + *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND; + *mbo_pos++ = 1; + /* Not Cellular aware */ + *mbo_pos++ = 0; + + if (hapd->mbo_assoc_disallow) { + *mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW; + *mbo_pos++ = 1; + *mbo_pos++ = hapd->mbo_assoc_disallow; + } + + pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo); + + return pos; +} + + +u8 hostapd_mbo_ie_len(struct hostapd_data *hapd) +{ + if (!hapd->conf->mbo_enabled) + return 0; + + /* + * MBO IE header (6) + Capability Indication attribute (3) + + * Association Disallowed attribute (3) = 12 + */ + return 6 + 3 + (hapd->mbo_assoc_disallow ? 3 : 0); +} + +#endif /* CONFIG_MBO */ + + +void ap_copy_sta_supp_op_classes(struct sta_info *sta, + const u8 *supp_op_classes, + size_t supp_op_classes_len) +{ + if (!supp_op_classes) + return; + os_free(sta->supp_op_classes); + sta->supp_op_classes = os_malloc(1 + supp_op_classes_len); + if (!sta->supp_op_classes) + return; + + sta->supp_op_classes[0] = supp_op_classes_len; + os_memcpy(sta->supp_op_classes + 1, supp_op_classes, + supp_op_classes_len); +} diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c index 5bf1b5d72002a..f30f63bc57094 100644 --- a/src/ap/ieee802_11_vht.c +++ b/src/ap/ieee802_11_vht.c @@ -17,9 +17,10 @@ #include "sta_info.h" #include "beacon.h" #include "ieee802_11.h" +#include "dfs.h" -u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) +u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts) { struct ieee80211_vht_capabilities *cap; struct hostapd_hw_modes *mode = hapd->iface->current_mode; @@ -49,6 +50,18 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) cap->vht_capabilities_info = host_to_le32( hapd->iface->conf->vht_capab); + if (nsts != 0) { + u32 hapd_nsts; + + hapd_nsts = le_to_host32(cap->vht_capabilities_info); + hapd_nsts = (hapd_nsts >> VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; + cap->vht_capabilities_info &= + ~(host_to_le32(hapd_nsts << + VHT_CAP_BEAMFORMEE_STS_OFFSET)); + cap->vht_capabilities_info |= + host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET); + } + /* Supported MCS set comes from hw */ os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8); @@ -80,6 +93,26 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) hapd->iconf->vht_oper_centr_freq_seg1_idx; oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth; + if (hapd->iconf->vht_oper_chwidth == 2) { + /* + * Convert 160 MHz channel width to new style as interop + * workaround. + */ + oper->vht_op_info_chwidth = 1; + oper->vht_op_info_chan_center_freq_seg1_idx = + oper->vht_op_info_chan_center_freq_seg0_idx; + if (hapd->iconf->channel < + hapd->iconf->vht_oper_centr_freq_seg0_idx) + oper->vht_op_info_chan_center_freq_seg0_idx -= 8; + else + oper->vht_op_info_chan_center_freq_seg0_idx += 8; + } else if (hapd->iconf->vht_oper_chwidth == 3) { + /* + * Convert 80+80 MHz channel width to new style as interop + * workaround. + */ + oper->vht_op_info_chwidth = 1; + } /* VHT Basic MCS set comes from hw */ /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */ @@ -131,6 +164,171 @@ static int check_valid_vht_mcs(struct hostapd_hw_modes *mode, } +u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid) +{ + u8 bw, chan1, chan2 = 0; + int freq1; + + if (!hapd->cs_freq_params.channel || + !hapd->cs_freq_params.vht_enabled) + return eid; + + /* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */ + switch (hapd->cs_freq_params.bandwidth) { + case 40: + bw = 0; + break; + case 80: + /* check if it's 80+80 */ + if (!hapd->cs_freq_params.center_freq2) + bw = 1; + else + bw = 3; + break; + case 160: + bw = 2; + break; + default: + /* not valid VHT bandwidth or not in CSA */ + return eid; + } + + freq1 = hapd->cs_freq_params.center_freq1 ? + hapd->cs_freq_params.center_freq1 : + hapd->cs_freq_params.freq; + if (ieee80211_freq_to_chan(freq1, &chan1) != + HOSTAPD_MODE_IEEE80211A) + return eid; + + if (hapd->cs_freq_params.center_freq2 && + ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2, + &chan2) != HOSTAPD_MODE_IEEE80211A) + return eid; + + *eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER; + *eid++ = 5; /* Length of Channel Switch Wrapper */ + *eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH; + *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */ + *eid++ = bw; /* New Channel Width */ + *eid++ = chan1; /* New Channel Center Frequency Segment 0 */ + *eid++ = chan2; /* New Channel Center Frequency Segment 1 */ + + return eid; +} + + +u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid) +{ + struct hostapd_iface *iface = hapd->iface; + struct hostapd_config *iconf = iface->conf; + struct hostapd_hw_modes *mode = iface->current_mode; + struct hostapd_channel_data *chan; + int dfs, i; + u8 channel, tx_pwr_count, local_pwr_constraint; + int max_tx_power; + u8 tx_pwr; + + if (!mode) + return eid; + + if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES) + return eid; + + for (i = 0; i < mode->num_channels; i++) { + if (mode->channels[i].freq == iface->freq) + break; + } + if (i == mode->num_channels) + return eid; + + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + if (iconf->secondary_channel == 0) { + /* Max Transmit Power count = 0 (20 MHz) */ + tx_pwr_count = 0; + } else { + /* Max Transmit Power count = 1 (20, 40 MHz) */ + tx_pwr_count = 1; + } + break; + case VHT_CHANWIDTH_80MHZ: + /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */ + tx_pwr_count = 2; + break; + case VHT_CHANWIDTH_80P80MHZ: + case VHT_CHANWIDTH_160MHZ: + /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */ + tx_pwr_count = 3; + break; + default: + return eid; + } + + /* + * Below local_pwr_constraint logic is referred from + * hostapd_eid_pwr_constraint. + * + * Check if DFS is required by regulatory. + */ + dfs = hostapd_is_dfs_required(hapd->iface); + if (dfs < 0) + dfs = 0; + + /* + * In order to meet regulations when TPC is not implemented using + * a transmit power that is below the legal maximum (including any + * mitigation factor) should help. In this case, indicate 3 dB below + * maximum allowed transmit power. + */ + if (hapd->iconf->local_pwr_constraint == -1) + local_pwr_constraint = (dfs == 0) ? 0 : 3; + else + local_pwr_constraint = hapd->iconf->local_pwr_constraint; + + /* + * A STA that is not an AP shall use a transmit power less than or + * equal to the local maximum transmit power level for the channel. + * The local maximum transmit power can be calculated from the formula: + * local max TX pwr = max TX pwr - local pwr constraint + * Where max TX pwr is maximum transmit power level specified for + * channel in Country element and local pwr constraint is specified + * for channel in this Power Constraint element. + */ + chan = &mode->channels[i]; + max_tx_power = chan->max_tx_power - local_pwr_constraint; + + /* + * Local Maximum Transmit power is encoded as two's complement + * with a 0.5 dB step. + */ + max_tx_power *= 2; /* in 0.5 dB steps */ + if (max_tx_power > 127) { + /* 63.5 has special meaning of 63.5 dBm or higher */ + max_tx_power = 127; + } + if (max_tx_power < -128) + max_tx_power = -128; + if (max_tx_power < 0) + tx_pwr = 0x80 + max_tx_power + 128; + else + tx_pwr = max_tx_power; + + *eid++ = WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE; + *eid++ = 2 + tx_pwr_count; + + /* + * Max Transmit Power count and + * Max Transmit Power units = 0 (EIRP) + */ + *eid++ = tx_pwr_count; + + for (i = 0; i <= tx_pwr_count; i++) + *eid++ = tx_pwr; + + return eid; +} + + u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_capab) { @@ -212,7 +410,7 @@ u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid) WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE); pos += 4; *pos++ = VENDOR_VHT_SUBTYPE; - pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_capabilities(hapd, pos, 0); pos = hostapd_eid_vht_operation(hapd, pos); return pos; diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index 0f2d428cf752a..80ff996948f9e 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -34,6 +34,9 @@ #include "ieee802_1x.h" +#ifdef CONFIG_HS20 +static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx); +#endif /* CONFIG_HS20 */ static void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, int success, int remediation); @@ -219,7 +222,7 @@ static void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) MAC2STR(sta->addr)); #ifndef CONFIG_NO_VLAN - if (sta->vlan_id > 0 && sta->vlan_id <= MAX_VLAN_ID) { + if (sta->vlan_id > 0) { wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported."); return; } @@ -402,7 +405,16 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, char buf[128]; if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_SERVICE_TYPE) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE, + RADIUS_SERVICE_TYPE_FRAMED)) { + wpa_printf(MSG_ERROR, "Could not add Service-Type"); + return -1; + } + + if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_NAS_PORT) && + sta->aid > 0 && !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { wpa_printf(MSG_ERROR, "Could not add NAS-Port"); return -1; @@ -435,9 +447,9 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, return -1; } - if (sta->acct_session_id_hi || sta->acct_session_id_lo) { - os_snprintf(buf, sizeof(buf), "%08X-%08X", - sta->acct_session_id_hi, sta->acct_session_id_lo); + if (sta->acct_session_id) { + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) sta->acct_session_id); if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, (u8 *) buf, os_strlen(buf))) { wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id"); @@ -445,6 +457,21 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, } } + if ((hapd->conf->wpa & 2) && + !hapd->conf->disable_pmksa_caching && + sta->eapol_sm && sta->eapol_sm->acct_multi_session_id) { + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) + sta->eapol_sm->acct_multi_session_id); + if (!radius_msg_add_attr( + msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_INFO, + "Could not add Acct-Multi-Session-Id"); + return -1; + } + } + #ifdef CONFIG_IEEE80211R if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && sta->wpa_sm && @@ -475,6 +502,7 @@ int add_common_radius_attr(struct hostapd_data *hapd, { char buf[128]; struct hostapd_radius_attr *attr; + int len; if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_NAS_IP_ADDRESS) && @@ -506,15 +534,15 @@ int add_common_radius_attr(struct hostapd_data *hapd, return -1; } - os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), - wpa_ssid_txt(hapd->conf->ssid.ssid, - hapd->conf->ssid.ssid_len)); - buf[sizeof(buf) - 1] = '\0'; + len = os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":", + MAC2STR(hapd->own_addr)); + os_memcpy(&buf[len], hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len); + len += hapd->conf->ssid.ssid_len; if (!hostapd_config_get_radius_attr(req_attr, RADIUS_ATTR_CALLED_STATION_ID) && !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, os_strlen(buf))) { + (u8 *) buf, len)) { wpa_printf(MSG_ERROR, "Could not add Called-Station-Id"); return -1; } @@ -583,7 +611,10 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, return; } - radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); + if (radius_msg_make_authenticator(msg) < 0) { + wpa_printf(MSG_INFO, "Could not make Request Authenticator"); + goto fail; + } if (sm->identity && !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, @@ -831,6 +862,29 @@ ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta) } +static void ieee802_1x_save_eapol(struct sta_info *sta, const u8 *buf, + size_t len) +{ + if (sta->pending_eapol_rx) { + wpabuf_free(sta->pending_eapol_rx->buf); + } else { + sta->pending_eapol_rx = + os_malloc(sizeof(*sta->pending_eapol_rx)); + if (!sta->pending_eapol_rx) + return; + } + + sta->pending_eapol_rx->buf = wpabuf_alloc_copy(buf, len); + if (!sta->pending_eapol_rx->buf) { + os_free(sta->pending_eapol_rx); + sta->pending_eapol_rx = NULL; + return; + } + + os_get_reltime(&sta->pending_eapol_rx->rx_time); +} + + /** * ieee802_1x_receive - Process the EAPOL frames from the Supplicant * @hapd: hostapd BSS data @@ -861,6 +915,13 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) { wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not " "associated/Pre-authenticating STA"); + + if (sta && (sta->flags & WLAN_STA_AUTH)) { + wpa_printf(MSG_DEBUG, "Saving EAPOL frame from " MACSTR + " for later use", MAC2STR(sta->addr)); + ieee802_1x_save_eapol(sta, buf, len); + } + return; } @@ -1047,7 +1108,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) * Clear any possible EAPOL authenticator state to support * reassociation change from WPS to PSK. */ - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); return; } @@ -1058,7 +1119,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) * Clear any possible EAPOL authenticator state to support * reassociation change from WPA-EAP to PSK. */ - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); return; } @@ -1106,6 +1167,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; sta->eapol_sm->authSuccess = TRUE; sta->eapol_sm->authFail = FALSE; + sta->eapol_sm->portValid = TRUE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); /* TODO: get vlan_id from R0KH using RRB message */ @@ -1128,7 +1190,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->authFail = FALSE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); - pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm); + pmksa_cache_to_eapol_data(hapd, pmksa, sta->eapol_sm); ap_sta_bind_vlan(hapd, sta); } else { if (reassoc) { @@ -1144,10 +1206,20 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) } -void ieee802_1x_free_station(struct sta_info *sta) +void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta) { struct eapol_state_machine *sm = sta->eapol_sm; +#ifdef CONFIG_HS20 + eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta); +#endif /* CONFIG_HS20 */ + + if (sta->pending_eapol_rx) { + wpabuf_free(sta->pending_eapol_rx->buf); + os_free(sta->pending_eapol_rx); + sta->pending_eapol_rx = NULL; + } + if (sm == NULL) return; @@ -1156,10 +1228,8 @@ void ieee802_1x_free_station(struct sta_info *sta) #ifndef CONFIG_NO_RADIUS radius_msg_free(sm->last_recv_radius); radius_free_class(&sm->radius_class); - wpabuf_free(sm->radius_cui); #endif /* CONFIG_NO_RADIUS */ - os_free(sm->identity); eapol_auth_free(sm); } @@ -1592,10 +1662,16 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, struct hostapd_data *hapd = data; struct sta_info *sta; u32 session_timeout = 0, termination_action, acct_interim_interval; - int session_timeout_set, vlan_id = 0; + int session_timeout_set; 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) { @@ -1659,27 +1735,32 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, switch (hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: - if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) - vlan_id = 0; #ifndef CONFIG_NO_VLAN - else - vlan_id = radius_msg_get_vlanid(msg); - if (vlan_id > 0 && - hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, - "VLAN ID %d", vlan_id); - } else if (vlan_id > 0) { + 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 ID %d received from RADIUS server", - vlan_id); + "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; - } else if (hapd->conf->ssid.dynamic_vlan == - DYNAMIC_VLAN_REQUIRED) { + } + + 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, @@ -1690,7 +1771,18 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, } #endif /* CONFIG_NO_VLAN */ - sta->vlan_id = vlan_id; + if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 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; @@ -1715,15 +1807,6 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, ieee802_1x_check_hs20(hapd, sta, msg, session_timeout_set ? (int) session_timeout : -1); - if (sm->eap_if->eapKeyAvailable && !sta->remediation && - !sta->hs20_deauth_requested && - wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, - session_timeout_set ? - (int) session_timeout : -1, sm) == 0) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "Added PMKSA cache entry"); - } break; case RADIUS_CODE_ACCESS_REJECT: sm->eap_if->aaaFail = TRUE; @@ -2190,7 +2273,7 @@ void ieee802_1x_deinit(struct hostapd_data *hapd) { eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL); - if (hapd->driver != NULL && + if (hapd->driver && hapd->drv_priv && (hapd->conf->ieee802_1x || hapd->conf->wpa)) hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); @@ -2495,12 +2578,12 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, /* TODO: dot1xAuthSessionOctetsTx */ /* TODO: dot1xAuthSessionFramesRx */ /* TODO: dot1xAuthSessionFramesTx */ - "dot1xAuthSessionId=%08X-%08X\n" + "dot1xAuthSessionId=%016llX\n" "dot1xAuthSessionAuthenticMethod=%d\n" "dot1xAuthSessionTime=%u\n" "dot1xAuthSessionTerminateCause=999\n" "dot1xAuthSessionUserName=%s\n", - sta->acct_session_id_hi, sta->acct_session_id_lo, + (unsigned long long) sta->acct_session_id, (wpa_key_mgmt_wpa_ieee8021x( wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? 1 : 2, @@ -2510,11 +2593,11 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, return len; len += ret; - if (sm->acct_multi_session_id_hi) { + if (sm->acct_multi_session_id) { ret = os_snprintf(buf + len, buflen - len, - "authMultiSessionId=%08X+%08X\n", - sm->acct_multi_session_id_hi, - sm->acct_multi_session_id_lo); + "authMultiSessionId=%016llX\n", + (unsigned long long) + sm->acct_multi_session_id); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2535,6 +2618,34 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, } +#ifdef CONFIG_HS20 +static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + if (sta->remediation) { + wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " + MACSTR " to indicate Subscription Remediation", + MAC2STR(sta->addr)); + hs20_send_wnm_notification(hapd, sta->addr, + sta->remediation_method, + sta->remediation_url); + os_free(sta->remediation_url); + sta->remediation_url = NULL; + } + + if (sta->hs20_deauth_req) { + wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " + MACSTR " to indicate imminent deauthentication", + MAC2STR(sta->addr)); + hs20_send_wnm_notification_deauth_req(hapd, sta->addr, + sta->hs20_deauth_req); + } +} +#endif /* CONFIG_HS20 */ + + static void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, int success, int remediation) @@ -2554,26 +2665,12 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, sta->remediation_method = 1; /* SOAP-XML SPP */ } - if (success) { - if (sta->remediation) { - wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification " - "to " MACSTR " to indicate Subscription " - "Remediation", - MAC2STR(sta->addr)); - hs20_send_wnm_notification(hapd, sta->addr, - sta->remediation_method, - sta->remediation_url); - os_free(sta->remediation_url); - sta->remediation_url = NULL; - } - - if (sta->hs20_deauth_req) { - wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification " - "to " MACSTR " to indicate imminent " - "deauthentication", MAC2STR(sta->addr)); - hs20_send_wnm_notification_deauth_req( - hapd, sta->addr, sta->hs20_deauth_req); - } + if (success && (sta->remediation || sta->hs20_deauth_req)) { + wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to " + MACSTR " in 100 ms", MAC2STR(sta->addr)); + eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta); + eloop_register_timeout(0, 100000, ieee802_1x_wnm_notif_send, + hapd, sta); } #endif /* CONFIG_HS20 */ @@ -2584,7 +2681,7 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, session_timeout = dot11RSNAConfigPMKLifetime; if (success && key && len >= PMK_LEN && !sta->remediation && !sta->hs20_deauth_requested && - wpa_auth_pmksa_add(sta->wpa_sm, key, session_timeout, + wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout, sta->eapol_sm) == 0) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h index 14d69556993c4..ec80199007b68 100644 --- a/src/ap/ieee802_1x.h +++ b/src/ap/ieee802_1x.h @@ -21,7 +21,7 @@ struct radius_msg; void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, size_t len); void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta); -void ieee802_1x_free_station(struct sta_info *sta); +void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta); void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, diff --git a/src/ap/mbo_ap.c b/src/ap/mbo_ap.c new file mode 100644 index 0000000000000..43b0bf16934e7 --- /dev/null +++ b/src/ap/mbo_ap.c @@ -0,0 +1,244 @@ +/* + * hostapd - MBO + * Copyright (c) 2016, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "hostapd.h" +#include "sta_info.h" +#include "mbo_ap.h" + + +void mbo_ap_sta_free(struct sta_info *sta) +{ + struct mbo_non_pref_chan_info *info, *prev; + + info = sta->non_pref_chan; + sta->non_pref_chan = NULL; + while (info) { + prev = info; + info = info->next; + os_free(prev); + } +} + + +static void mbo_ap_parse_non_pref_chan(struct sta_info *sta, + const u8 *buf, size_t len) +{ + struct mbo_non_pref_chan_info *info, *tmp; + char channels[200], *pos, *end; + size_t num_chan, i; + int ret; + + if (len <= 3) + return; /* Not enough room for any channels */ + + num_chan = len - 3; + info = os_zalloc(sizeof(*info) + num_chan); + if (!info) + return; + info->op_class = buf[0]; + info->pref = buf[len - 2]; + info->reason_code = buf[len - 1]; + info->num_channels = num_chan; + buf++; + os_memcpy(info->channels, buf, num_chan); + if (!sta->non_pref_chan) { + sta->non_pref_chan = info; + } else { + tmp = sta->non_pref_chan; + while (tmp->next) + tmp = tmp->next; + tmp->next = info; + } + + pos = channels; + end = pos + sizeof(channels); + *pos = '\0'; + for (i = 0; i < num_chan; i++) { + ret = os_snprintf(pos, end - pos, "%s%u", + i == 0 ? "" : " ", buf[i]); + if (os_snprintf_error(end - pos, ret)) { + *pos = '\0'; + break; + } + pos += ret; + } + + wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR + " non-preferred channel list (op class %u, pref %u, reason code %u, channels %s)", + MAC2STR(sta->addr), info->op_class, info->pref, + info->reason_code, channels); +} + + +void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta, + struct ieee802_11_elems *elems) +{ + const u8 *pos, *attr, *end; + size_t len; + + if (!hapd->conf->mbo_enabled || !elems->mbo) + return; + + pos = elems->mbo + 4; + len = elems->mbo_len - 4; + wpa_hexdump(MSG_DEBUG, "MBO: Association Request attributes", pos, len); + + attr = get_ie(pos, len, MBO_ATTR_ID_CELL_DATA_CAPA); + if (attr && attr[1] >= 1) + sta->cell_capa = attr[2]; + + mbo_ap_sta_free(sta); + end = pos + len; + while (end - pos > 1) { + u8 ie_len = pos[1]; + + if (2 + ie_len > end - pos) + break; + + if (pos[0] == MBO_ATTR_ID_NON_PREF_CHAN_REPORT) + mbo_ap_parse_non_pref_chan(sta, pos + 2, ie_len); + pos += 2 + pos[1]; + } +} + + +int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen) +{ + char *pos = buf, *end = buf + buflen; + int ret; + struct mbo_non_pref_chan_info *info; + u8 i; + unsigned int count = 0; + + if (!sta->cell_capa) + return 0; + + ret = os_snprintf(pos, end - pos, "mbo_cell_capa=%u\n", sta->cell_capa); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + for (info = sta->non_pref_chan; info; info = info->next) { + char *pos2 = pos; + + ret = os_snprintf(pos2, end - pos2, + "non_pref_chan[%u]=%u:%u:%u:", + count, info->op_class, info->pref, + info->reason_code); + count++; + if (os_snprintf_error(end - pos2, ret)) + break; + pos2 += ret; + + for (i = 0; i < info->num_channels; i++) { + ret = os_snprintf(pos2, end - pos2, "%u%s", + info->channels[i], + i + 1 < info->num_channels ? + "," : ""); + if (os_snprintf_error(end - pos2, ret)) { + pos2 = NULL; + break; + } + pos2 += ret; + } + + if (!pos2) + break; + ret = os_snprintf(pos2, end - pos2, "\n"); + if (os_snprintf_error(end - pos2, ret)) + break; + pos2 += ret; + pos = pos2; + } + + return pos - buf; +} + + +static void mbo_ap_wnm_notif_req_cell_capa(struct sta_info *sta, + const u8 *buf, size_t len) +{ + if (len < 1) + return; + wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR + " updated cellular data capability: %u", + MAC2STR(sta->addr), buf[0]); + sta->cell_capa = buf[0]; +} + + +static void mbo_ap_wnm_notif_req_elem(struct sta_info *sta, u8 type, + const u8 *buf, size_t len, + int *first_non_pref_chan) +{ + switch (type) { + case WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT: + if (*first_non_pref_chan) { + /* + * Need to free the previously stored entries now to + * allow the update to replace all entries. + */ + *first_non_pref_chan = 0; + mbo_ap_sta_free(sta); + } + mbo_ap_parse_non_pref_chan(sta, buf, len); + break; + case WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA: + mbo_ap_wnm_notif_req_cell_capa(sta, buf, len); + break; + default: + wpa_printf(MSG_DEBUG, + "MBO: Ignore unknown WNM Notification WFA subelement %u", + type); + break; + } +} + + +void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr, + const u8 *buf, size_t len) +{ + const u8 *pos, *end; + u8 ie_len; + struct sta_info *sta; + int first_non_pref_chan = 1; + + if (!hapd->conf->mbo_enabled) + return; + + sta = ap_get_sta(hapd, addr); + if (!sta) + return; + + pos = buf; + end = buf + len; + + while (end - pos > 1) { + ie_len = pos[1]; + + if (2 + ie_len > end - pos) + break; + + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && + ie_len >= 4 && WPA_GET_BE24(pos + 2) == OUI_WFA) + mbo_ap_wnm_notif_req_elem(sta, pos[5], + pos + 6, ie_len - 4, + &first_non_pref_chan); + else + wpa_printf(MSG_DEBUG, + "MBO: Ignore unknown WNM Notification element %u (len=%u)", + pos[0], pos[1]); + + pos += 2 + pos[1]; + } +} diff --git a/src/ap/mbo_ap.h b/src/ap/mbo_ap.h new file mode 100644 index 0000000000000..9f37f2802f3bc --- /dev/null +++ b/src/ap/mbo_ap.h @@ -0,0 +1,51 @@ +/* + * MBO related functions and structures + * Copyright (c) 2016, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MBO_AP_H +#define MBO_AP_H + +struct hostapd_data; +struct sta_info; +struct ieee802_11_elems; + +#ifdef CONFIG_MBO + +void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta, + struct ieee802_11_elems *elems); +int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen); +void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr, + const u8 *buf, size_t len); +void mbo_ap_sta_free(struct sta_info *sta); + +#else /* CONFIG_MBO */ + +static inline void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, + struct sta_info *sta, + struct ieee802_11_elems *elems) +{ +} + +static inline int mbo_ap_get_info(struct sta_info *sta, char *buf, + size_t buflen) +{ + return 0; +} + +static inline void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, + const u8 *addr, + const u8 *buf, size_t len) +{ +} + +static inline void mbo_ap_sta_free(struct sta_info *sta) +{ +} + +#endif /* CONFIG_MBO */ + +#endif /* MBO_AP_H */ diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c index 4a87721e2ecfc..3c086bfc7131b 100644 --- a/src/ap/ndisc_snoop.c +++ b/src/ap/ndisc_snoop.c @@ -17,6 +17,7 @@ #include "ap_drv_ops.h" #include "list.h" #include "x_snoop.h" +#include "ndisc_snoop.h" struct ip6addr { struct in6_addr addr; diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c new file mode 100644 index 0000000000000..a2efff6182868 --- /dev/null +++ b/src/ap/neighbor_db.c @@ -0,0 +1,133 @@ +/* + * hostapd / Neighboring APs DB + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * + * 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 "hostapd.h" +#include "neighbor_db.h" + + +struct hostapd_neighbor_entry * +hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid) +{ + struct hostapd_neighbor_entry *nr; + + dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, + list) { + if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 && + (!ssid || + (ssid->ssid_len == nr->ssid.ssid_len && + os_memcmp(ssid->ssid, nr->ssid.ssid, + ssid->ssid_len) == 0))) + return nr; + } + return NULL; +} + + +static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr) +{ + wpabuf_free(nr->nr); + nr->nr = NULL; + wpabuf_free(nr->lci); + nr->lci = NULL; + wpabuf_free(nr->civic); + nr->civic = NULL; + os_memset(nr->bssid, 0, sizeof(nr->bssid)); + os_memset(&nr->ssid, 0, sizeof(nr->ssid)); +} + + +static struct hostapd_neighbor_entry * +hostapd_neighbor_add(struct hostapd_data *hapd) +{ + struct hostapd_neighbor_entry *nr; + + nr = os_zalloc(sizeof(struct hostapd_neighbor_entry)); + if (!nr) + return NULL; + + dl_list_add(&hapd->nr_db, &nr->list); + + return nr; +} + + +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) +{ + struct hostapd_neighbor_entry *entry; + + entry = hostapd_neighbor_get(hapd, bssid, ssid); + if (!entry) + entry = hostapd_neighbor_add(hapd); + if (!entry) + return -1; + + hostapd_neighbor_clear_entry(entry); + + os_memcpy(entry->bssid, bssid, ETH_ALEN); + os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid)); + + entry->nr = wpabuf_dup(nr); + if (!entry->nr) + goto fail; + + if (lci) { + entry->lci = wpabuf_dup(lci); + if (!entry->lci || os_get_time(&entry->lci_date)) + goto fail; + } + + if (civic) { + entry->civic = wpabuf_dup(civic); + if (!entry->civic) + goto fail; + } + + return 0; + +fail: + hostapd_neighbor_remove(hapd, bssid, ssid); + return -1; +} + + +int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid) +{ + struct hostapd_neighbor_entry *nr; + + nr = hostapd_neighbor_get(hapd, bssid, ssid); + if (!nr) + return -1; + + hostapd_neighbor_clear_entry(nr); + dl_list_del(&nr->list); + os_free(nr); + + return 0; +} + + +void hostpad_free_neighbor_db(struct hostapd_data *hapd) +{ + struct hostapd_neighbor_entry *nr, *prev; + + dl_list_for_each_safe(nr, prev, &hapd->nr_db, + struct hostapd_neighbor_entry, list) { + hostapd_neighbor_clear_entry(nr); + dl_list_del(&nr->list); + os_free(nr); + } +} diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h new file mode 100644 index 0000000000000..c22e043c120ef --- /dev/null +++ b/src/ap/neighbor_db.h @@ -0,0 +1,24 @@ +/* + * hostapd / Neighboring APs DB + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NEIGHBOR_DB_H +#define NEIGHBOR_DB_H + +struct hostapd_neighbor_entry * +hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid, + const struct wpa_ssid_value *ssid); +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 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); + +#endif /* NEIGHBOR_DB_H */ diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c index 877affe4eadcc..d610e7e5b0057 100644 --- a/src/ap/pmksa_cache_auth.c +++ b/src/ap/pmksa_cache_auth.c @@ -38,6 +38,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) { + os_free(entry->vlan_desc); os_free(entry->identity); wpabuf_free(entry->cui); #ifndef CONFIG_NO_RADIUS @@ -91,6 +92,20 @@ void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, } +/** + * pmksa_cache_auth_flush - Flush all PMKSA cache entries + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + */ +void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa) +{ + while (pmksa->pmksa) { + wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry for " + MACSTR, MAC2STR(pmksa->pmksa->spa)); + pmksa_cache_free_entry(pmksa, pmksa->pmksa); + } +} + + static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; @@ -126,6 +141,8 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, struct eapol_state_machine *eapol) { + struct vlan_description *vlan_desc; + if (eapol == NULL) return; @@ -146,14 +163,22 @@ static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, #endif /* CONFIG_NO_RADIUS */ entry->eap_type_authsrv = eapol->eap_type_authsrv; - entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id; - entry->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi; - entry->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo; + vlan_desc = ((struct sta_info *) eapol->sta)->vlan_desc; + if (vlan_desc && vlan_desc->notempty) { + entry->vlan_desc = os_zalloc(sizeof(struct vlan_description)); + if (entry->vlan_desc) + *entry->vlan_desc = *vlan_desc; + } else { + entry->vlan_desc = NULL; + } + + entry->acct_multi_session_id = eapol->acct_multi_session_id; } -void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, +void pmksa_cache_to_eapol_data(struct hostapd_data *hapd, + struct rsn_pmksa_cache_entry *entry, struct eapol_state_machine *eapol) { if (entry == NULL || eapol == NULL) @@ -186,10 +211,11 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, } eapol->eap_type_authsrv = entry->eap_type_authsrv; - ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id; +#ifndef CONFIG_NO_VLAN + ap_sta_set_vlan(hapd, eapol->sta, entry->vlan_desc); +#endif /* CONFIG_NO_VLAN */ - eapol->acct_multi_session_id_hi = entry->acct_multi_session_id_hi; - eapol->acct_multi_session_id_lo = entry->acct_multi_session_id_lo; + eapol->acct_multi_session_id = entry->acct_multi_session_id; } @@ -234,6 +260,7 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() * @pmk: The new pairwise master key * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @pmkid: Calculated PMKID * @kck: Key confirmation key or %NULL if not yet derived * @kck_len: KCK length in bytes * @aa: Authenticator address @@ -250,7 +277,7 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, */ struct rsn_pmksa_cache_entry * pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, - const u8 *pmk, size_t pmk_len, + const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, int session_timeout, struct eapol_state_machine *eapol, int akmp) @@ -258,7 +285,7 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry, *pos; struct os_reltime now; - if (pmk_len > PMK_LEN) + if (pmk_len > PMK_LEN_MAX) return NULL; if (wpa_key_mgmt_suite_b(akmp) && !kck) @@ -269,7 +296,9 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, return NULL; os_memcpy(entry->pmk, pmk, pmk_len); entry->pmk_len = pmk_len; - if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + if (pmkid) + os_memcpy(entry->pmkid, pmkid, PMKID_LEN); + else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid); else if (wpa_key_mgmt_suite_b(akmp)) rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); @@ -337,7 +366,13 @@ pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, radius_copy_class(&entry->radius_class, &old_entry->radius_class); #endif /* CONFIG_NO_RADIUS */ entry->eap_type_authsrv = old_entry->eap_type_authsrv; - entry->vlan_id = old_entry->vlan_id; + if (old_entry->vlan_desc) { + entry->vlan_desc = os_zalloc(sizeof(struct vlan_description)); + if (entry->vlan_desc) + *entry->vlan_desc = *old_entry->vlan_desc; + } else { + entry->vlan_desc = NULL; + } entry->opportunistic = 1; pmksa_cache_link_entry(pmksa, entry); @@ -471,12 +506,11 @@ static int das_attr_match(struct rsn_pmksa_cache_entry *entry, if (attr->acct_multi_session_id) { char buf[20]; - if (attr->acct_multi_session_id_len != 17) + if (attr->acct_multi_session_id_len != 16) return 0; - os_snprintf(buf, sizeof(buf), "%08X+%08X", - entry->acct_multi_session_id_hi, - entry->acct_multi_session_id_lo); - if (os_memcmp(attr->acct_multi_session_id, buf, 17) != 0) + os_snprintf(buf, sizeof(buf), "%016llX", + (unsigned long long) entry->acct_multi_session_id); + if (os_memcmp(attr->acct_multi_session_id, buf, 16) != 0) return 0; match++; } @@ -526,3 +560,48 @@ int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, return found ? 0 : -1; } + + +/** + * pmksa_cache_auth_list - Dump text list of entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @buf: Buffer for the list + * @len: Length of the buffer + * Returns: Number of bytes written to buffer + * + * This function is used to generate a text format representation of the + * current PMKSA cache contents for the ctrl_iface PMKSA command. + */ +int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) +{ + int i, ret; + char *pos = buf; + struct rsn_pmksa_cache_entry *entry; + struct os_reltime now; + + os_get_reltime(&now); + ret = os_snprintf(pos, buf + len - pos, + "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n"); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + i = 0; + entry = pmksa->pmksa; + while (entry) { + ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", + i, MAC2STR(entry->spa)); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, + PMKID_LEN); + ret = os_snprintf(pos, buf + len - pos, " %d %d\n", + (int) (entry->expiration - now.sec), + entry->opportunistic); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + entry = entry->next; + } + return pos - buf; +} diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h index 8b7be1291b53a..d8d9c5a25c0e4 100644 --- a/src/ap/pmksa_cache_auth.h +++ b/src/ap/pmksa_cache_auth.h @@ -17,7 +17,7 @@ struct rsn_pmksa_cache_entry { struct rsn_pmksa_cache_entry *next, *hnext; u8 pmkid[PMKID_LEN]; - u8 pmk[PMK_LEN]; + u8 pmk[PMK_LEN_MAX]; size_t pmk_len; os_time_t expiration; int akmp; /* WPA_KEY_MGMT_* */ @@ -28,11 +28,10 @@ struct rsn_pmksa_cache_entry { struct wpabuf *cui; struct radius_class_data radius_class; u8 eap_type_authsrv; - int vlan_id; + struct vlan_description *vlan_desc; int opportunistic; - u32 acct_multi_session_id_hi; - u32 acct_multi_session_id_lo; + u64 acct_multi_session_id; }; struct rsn_pmksa_cache; @@ -49,7 +48,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( const u8 *pmkid); struct rsn_pmksa_cache_entry * pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, - const u8 *pmk, size_t pmk_len, + const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, int session_timeout, struct eapol_state_machine *eapol, int akmp); @@ -57,11 +56,14 @@ struct rsn_pmksa_cache_entry * pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, const struct rsn_pmksa_cache_entry *old_entry, const u8 *aa, const u8 *pmkid); -void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, +void pmksa_cache_to_eapol_data(struct hostapd_data *hapd, + struct rsn_pmksa_cache_entry *entry, struct eapol_state_machine *eapol); void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry); int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, struct radius_das_attrs *attr); +int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); +void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa); #endif /* PMKSA_CACHE_H */ diff --git a/src/ap/rrm.c b/src/ap/rrm.c new file mode 100644 index 0000000000000..3569f955bcd27 --- /dev/null +++ b/src/ap/rrm.c @@ -0,0 +1,544 @@ +/* + * hostapd / Radio Measurement (RRM) + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * + * 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 "hostapd.h" +#include "ap_drv_ops.h" +#include "sta_info.h" +#include "eloop.h" +#include "neighbor_db.h" +#include "rrm.h" + +#define HOSTAPD_RRM_REQUEST_TIMEOUT 5 + + +static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct hostapd_data *hapd = eloop_data; + + wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out", + hapd->lci_req_token); + hapd->lci_req_active = 0; +} + + +static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token, + const u8 *pos, size_t len) +{ + if (!hapd->lci_req_active || hapd->lci_req_token != token) { + wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token); + return; + } + + hapd->lci_req_active = 0; + eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL); + wpa_printf(MSG_DEBUG, "LCI report token %u len %zu", token, len); +} + + +static void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct hostapd_data *hapd = eloop_data; + + wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out", + hapd->range_req_token); + hapd->range_req_active = 0; +} + + +static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token, + const u8 *pos, size_t len) +{ + if (!hapd->range_req_active || hapd->range_req_token != token) { + wpa_printf(MSG_DEBUG, "Unexpected range report, token %u", + token); + return; + } + + hapd->range_req_active = 0; + eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); + wpa_printf(MSG_DEBUG, "Range report token %u len %zu", token, len); +} + + +static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd, + const u8 *buf, size_t len) +{ + const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; + const u8 *pos, *ie, *end; + u8 token; + + end = buf + len; + token = mgmt->u.action.u.rrm.dialog_token; + pos = mgmt->u.action.u.rrm.variable; + + while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) { + if (ie[1] < 5) { + wpa_printf(MSG_DEBUG, "Bad Measurement Report element"); + break; + } + + wpa_printf(MSG_DEBUG, "Measurement report type %u", ie[4]); + + switch (ie[4]) { + case MEASURE_TYPE_LCI: + hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]); + break; + case MEASURE_TYPE_FTM_RANGE: + hostapd_handle_range_report(hapd, token, ie + 2, ie[1]); + break; + default: + wpa_printf(MSG_DEBUG, + "Measurement report type %u is not supported", + ie[4]); + break; + } + + pos = ie + ie[1] + 2; + } +} + + +static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len) +{ + const u8 *subelem; + + /* Range Request element + Location Subject + Maximum Age subelement */ + if (len < 3 + 1 + 4) + return 0; + + /* Subelements are arranged as IEs */ + subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE); + if (subelem && subelem[1] == 2) + return *(u16 *) (subelem + 2); + + return 0; +} + + +static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age) +{ + struct os_time curr, diff; + unsigned long diff_l; + + if (!max_age) + return 0; + + if (max_age == 0xffff) + return 1; + + if (os_get_time(&curr)) + return 0; + + os_time_sub(&curr, &nr->lci_date, &diff); + + /* avoid overflow */ + if (diff.sec > 0xffff) + return 0; + + /* LCI age is calculated in 10th of a second units. */ + diff_l = diff.sec * 10 + diff.usec / 100000; + + return max_age > diff_l; +} + + +static size_t hostapd_neighbor_report_len(struct wpabuf *buf, + struct hostapd_neighbor_entry *nr, + int send_lci, int send_civic) +{ + size_t len = 2 + wpabuf_len(nr->nr); + + if (send_lci && nr->lci) + len += 2 + wpabuf_len(nr->lci); + + if (send_civic && nr->civic) + len += 2 + wpabuf_len(nr->civic); + + return len; +} + + +static void hostapd_send_nei_report_resp(struct hostapd_data *hapd, + const u8 *addr, u8 dialog_token, + struct wpa_ssid_value *ssid, u8 lci, + u8 civic, u16 lci_max_age) +{ + struct hostapd_neighbor_entry *nr; + struct wpabuf *buf; + u8 *msmt_token; + + /* + * The number and length of the Neighbor Report elements in a Neighbor + * Report frame is limited by the maximum allowed MMPDU size; + 3 bytes + * of RRM header. + */ + buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE); + if (!buf) + return; + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE); + wpabuf_put_u8(buf, dialog_token); + + dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, + list) { + int send_lci; + size_t len; + + if (ssid->ssid_len != nr->ssid.ssid_len || + os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0) + continue; + + send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age); + len = hostapd_neighbor_report_len(buf, nr, send_lci, civic); + + if (len - 2 > 0xff) { + wpa_printf(MSG_DEBUG, + "NR entry for " MACSTR " exceeds 0xFF bytes", + MAC2STR(nr->bssid)); + continue; + } + + if (len > wpabuf_tailroom(buf)) + break; + + wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT); + wpabuf_put_u8(buf, len - 2); + wpabuf_put_buf(buf, nr->nr); + + if (send_lci && nr->lci) { + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT); + wpabuf_put_u8(buf, wpabuf_len(nr->lci)); + /* + * Override measurement token - the first byte of the + * Measurement Report element. + */ + msmt_token = wpabuf_put(buf, 0); + wpabuf_put_buf(buf, nr->lci); + *msmt_token = lci; + } + + if (civic && nr->civic) { + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT); + wpabuf_put_u8(buf, wpabuf_len(nr->civic)); + /* + * Override measurement token - the first byte of the + * Measurement Report element. + */ + msmt_token = wpabuf_put(buf, 0); + wpabuf_put_buf(buf, nr->civic); + *msmt_token = civic; + } + } + + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); +} + + +static void hostapd_handle_nei_report_req(struct hostapd_data *hapd, + const u8 *buf, size_t len) +{ + const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; + const u8 *pos, *ie, *end; + struct wpa_ssid_value ssid = { + .ssid_len = 0 + }; + u8 token; + u8 lci = 0, civic = 0; /* Measurement tokens */ + u16 lci_max_age = 0; + + if (!(hapd->conf->radio_measurements[0] & + WLAN_RRM_CAPS_NEIGHBOR_REPORT)) + return; + + end = buf + len; + + token = mgmt->u.action.u.rrm.dialog_token; + pos = mgmt->u.action.u.rrm.variable; + len = end - pos; + + ie = get_ie(pos, len, WLAN_EID_SSID); + if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) { + ssid.ssid_len = ie[1]; + os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len); + } else { + ssid.ssid_len = hapd->conf->ssid.ssid_len; + os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len); + } + + while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) { + if (ie[1] < 3) + break; + + wpa_printf(MSG_DEBUG, + "Neighbor report request, measure type %u", + ie[4]); + + switch (ie[4]) { /* Measurement Type */ + case MEASURE_TYPE_LCI: + lci = ie[2]; /* Measurement Token */ + lci_max_age = hostapd_parse_location_lci_req_age(ie + 2, + ie[1]); + break; + case MEASURE_TYPE_LOCATION_CIVIC: + civic = ie[2]; /* Measurement token */ + break; + } + + pos = ie + ie[1] + 2; + len = end - pos; + } + + hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic, + lci_max_age); +} + + +void hostapd_handle_radio_measurement(struct hostapd_data *hapd, + const u8 *buf, size_t len) +{ + const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; + + /* + * Check for enough bytes: header + (1B)Category + (1B)Action + + * (1B)Dialog Token. + */ + if (len < IEEE80211_HDRLEN + 3) + return; + + wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR, + mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa)); + + switch (mgmt->u.action.u.rrm.action) { + case WLAN_RRM_RADIO_MEASUREMENT_REPORT: + hostapd_handle_radio_msmt_report(hapd, buf, len); + break; + case WLAN_RRM_NEIGHBOR_REPORT_REQUEST: + hostapd_handle_nei_report_req(hapd, buf, len); + break; + default: + wpa_printf(MSG_DEBUG, "RRM action %u is not supported", + mgmt->u.action.u.rrm.action); + break; + } +} + + +int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr) +{ + struct wpabuf *buf; + struct sta_info *sta = ap_get_sta(hapd, addr); + int ret; + + if (!sta) { + wpa_printf(MSG_INFO, + "Request LCI: Destination address is not in station list"); + return -1; + } + + if (!(sta->flags & WLAN_STA_AUTHORIZED)) { + wpa_printf(MSG_INFO, + "Request LCI: Destination address is not connected"); + return -1; + } + + if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) { + wpa_printf(MSG_INFO, + "Request LCI: Station does not support LCI in RRM"); + return -1; + } + + if (hapd->lci_req_active) { + wpa_printf(MSG_DEBUG, + "Request LCI: LCI request is already in process, overriding"); + hapd->lci_req_active = 0; + eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, + NULL); + } + + /* Measurement request (5) + Measurement element with LCI (10) */ + buf = wpabuf_alloc(5 + 10); + if (!buf) + return -1; + + hapd->lci_req_token++; + /* For wraparounds - the token must be nonzero */ + if (!hapd->lci_req_token) + hapd->lci_req_token++; + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); + wpabuf_put_u8(buf, hapd->lci_req_token); + wpabuf_put_le16(buf, 0); /* Number of repetitions */ + + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, 3 + 1 + 4); + + wpabuf_put_u8(buf, 1); /* Measurement Token */ + /* + * Parallel and Enable bits are 0, Duration, Request, and Report are + * reserved. + */ + wpabuf_put_u8(buf, 0); + wpabuf_put_u8(buf, MEASURE_TYPE_LCI); + + wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); + + wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE); + wpabuf_put_u8(buf, 2); + wpabuf_put_le16(buf, 0xffff); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + if (ret) + return ret; + + hapd->lci_req_active = 1; + + eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, + hostapd_lci_rep_timeout_handler, hapd, NULL); + + return 0; +} + + +int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, + u16 random_interval, u8 min_ap, + const u8 *responders, unsigned int n_responders) +{ + struct wpabuf *buf; + struct sta_info *sta; + u8 *len; + unsigned int i; + int ret; + + wpa_printf(MSG_DEBUG, "Request range: dest addr " MACSTR + " rand interval %u min AP %u n_responders %u", MAC2STR(addr), + random_interval, min_ap, n_responders); + + if (min_ap == 0 || min_ap > n_responders) { + wpa_printf(MSG_INFO, "Request range: Wrong min AP count"); + return -1; + } + + sta = ap_get_sta(hapd, addr); + if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { + wpa_printf(MSG_INFO, + "Request range: Destination address is not connected"); + return -1; + } + + if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_FTM_RANGE_REPORT)) { + wpa_printf(MSG_ERROR, + "Request range: Destination station does not support FTM range report in RRM"); + return -1; + } + + if (hapd->range_req_active) { + wpa_printf(MSG_DEBUG, + "Request range: Range request is already in process; overriding"); + hapd->range_req_active = 0; + eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, + hostapd_range_rep_timeout_handler, hapd, + NULL); + } + + /* Action + measurement type + token + reps + EID + len = 7 */ + buf = wpabuf_alloc(7 + 255); + if (!buf) + return -1; + + hapd->range_req_token++; + if (!hapd->range_req_token) /* For wraparounds */ + hapd->range_req_token++; + + /* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */ + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); + wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */ + wpabuf_put_le16(buf, 0); /* Number of Repetitions */ + + /* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + len = wpabuf_put(buf, 1); /* Length will be set later */ + + wpabuf_put_u8(buf, 1); /* Measurement Token */ + /* + * Parallel and Enable bits are 0; Duration, Request, and Report are + * reserved. + */ + wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ + wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */ + + /* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */ + wpabuf_put_le16(buf, random_interval); /* Randomization Interval */ + wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */ + + /* FTM Range Subelements */ + + /* + * Taking the neighbor report part of the range request from neighbor + * database instead of requesting the separate bits of data from the + * user. + */ + for (i = 0; i < n_responders; i++) { + struct hostapd_neighbor_entry *nr; + + nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i, + NULL); + if (!nr) { + wpa_printf(MSG_INFO, "Missing neighbor report for " + MACSTR, MAC2STR(responders + ETH_ALEN * i)); + wpabuf_free(buf); + return -1; + } + + if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) { + wpa_printf(MSG_ERROR, "Too long range request"); + wpabuf_free(buf); + return -1; + } + + wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT); + wpabuf_put_u8(buf, wpabuf_len(nr->nr)); + wpabuf_put_buf(buf, nr->nr); + } + + /* Action + measurement type + token + reps + EID + len = 7 */ + *len = wpabuf_len(buf) - 7; + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + if (ret) + return ret; + + hapd->range_req_active = 1; + + eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, + hostapd_range_rep_timeout_handler, hapd, NULL); + + return 0; +} + + +void hostapd_clean_rrm(struct hostapd_data *hapd) +{ + hostpad_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); + hapd->range_req_active = 0; +} diff --git a/src/ap/rrm.h b/src/ap/rrm.h new file mode 100644 index 0000000000000..f07fd41ac0195 --- /dev/null +++ b/src/ap/rrm.h @@ -0,0 +1,28 @@ +/* + * hostapd / Radio Measurement (RRM) + * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. + * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RRM_H +#define RRM_H + +/* + * Max measure request length is 255, -6 of the body we have 249 for the + * neighbor report elements. Each neighbor report element is at least 2 + 13 + * bytes, so we can't have more than 16 responders in the request. + */ +#define RRM_RANGE_REQ_MAX_RESPONDERS 16 + +void hostapd_handle_radio_measurement(struct hostapd_data *hapd, + const u8 *buf, size_t len); +int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr); +int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, + u16 random_interval, u8 min_ap, + const u8 *responders, unsigned int n_responders); +void hostapd_clean_rrm(struct hostapd_data *hapd); + +#endif /* RRM_H */ diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index d64307ccfd085..f12d4088b1314 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -32,8 +32,10 @@ #include "ap_drv_ops.h" #include "gas_serv.h" #include "wnm_ap.h" +#include "mbo_ap.h" #include "ndisc_snoop.h" #include "sta_info.h" +#include "vlan.h" static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, struct sta_info *sta); @@ -169,21 +171,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) ap_sta_ip6addr_del(hapd, sta); if (!hapd->iface->driver_ap_teardown && - !(sta->flags & WLAN_STA_PREAUTH)) + !(sta->flags & WLAN_STA_PREAUTH)) { hostapd_drv_sta_remove(hapd, sta->addr); - -#ifndef CONFIG_NO_VLAN - if (sta->vlan_id_bound) { - /* - * Need to remove the STA entry before potentially removing the - * VLAN. - */ - if (hapd->iface->driver_ap_teardown && - !(sta->flags & WLAN_STA_PREAUTH)) - hostapd_drv_sta_remove(hapd, sta->addr); - vlan_remove_dynamic(hapd, sta->vlan_id_bound); + sta->added_unassoc = 0; } -#endif /* CONFIG_NO_VLAN */ ap_sta_hash_del(hapd, sta); ap_sta_list_del(hapd, sta); @@ -231,6 +222,13 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) hapd->iface->num_sta_ht_20mhz--; } +#ifdef CONFIG_TAXONOMY + wpabuf_free(sta->probe_ie_taxonomy); + sta->probe_ie_taxonomy = NULL; + wpabuf_free(sta->assoc_ie_taxonomy); + sta->assoc_ie_taxonomy = NULL; +#endif /* CONFIG_TAXONOMY */ + #ifdef CONFIG_IEEE80211N ht40_intolerant_remove(hapd->iface, sta); #endif /* CONFIG_IEEE80211N */ @@ -251,7 +249,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) #ifdef CONFIG_MESH if (hapd->mesh_sta_free_cb) - hapd->mesh_sta_free_cb(sta); + hapd->mesh_sta_free_cb(hapd, sta); #endif /* CONFIG_MESH */ if (set_beacon) @@ -262,11 +260,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta); - eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); - eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); + ap_sta_clear_disconnect_timeouts(hapd, sta); sae_clear_retransmit_timer(hapd, sta); - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); wpa_auth_sta_deinit(sta->wpa_sm); rsn_preauth_free_station(hapd, sta); #ifndef CONFIG_NO_RADIUS @@ -274,6 +271,28 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) radius_client_flush_auth(hapd->radius, sta->addr); #endif /* CONFIG_NO_RADIUS */ +#ifndef CONFIG_NO_VLAN + /* + * sta->wpa_sm->group needs to be released before so that + * vlan_remove_dynamic() can check that no stations are left on the + * AP_VLAN netdev. + */ + if (sta->vlan_id) + vlan_remove_dynamic(hapd, sta->vlan_id); + if (sta->vlan_id_bound) { + /* + * Need to remove the STA entry before potentially removing the + * VLAN. + */ + if (hapd->iface->driver_ap_teardown && + !(sta->flags & WLAN_STA_PREAUTH)) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } + vlan_remove_dynamic(hapd, sta->vlan_id_bound); + } +#endif /* CONFIG_NO_VLAN */ + os_free(sta->challenge); #ifdef CONFIG_IEEE80211W @@ -315,6 +334,9 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) os_free(sta->sae); #endif /* CONFIG_SAE */ + mbo_ap_sta_free(sta); + os_free(sta->supp_op_classes); + os_free(sta); } @@ -354,8 +376,8 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) unsigned long next_time = 0; int reason; - wpa_printf(MSG_DEBUG, "%s: " MACSTR " flags=0x%x timeout_next=%d", - __func__, MAC2STR(sta->addr), sta->flags, + wpa_printf(MSG_DEBUG, "%s: %s: " MACSTR " flags=0x%x timeout_next=%d", + hapd->conf->iface, __func__, MAC2STR(sta->addr), sta->flags, sta->timeout_next); if (sta->timeout_next == STA_REMOVE) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -482,7 +504,7 @@ skip_poll: sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated due to " "inactivity"); @@ -519,6 +541,8 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; + wpa_printf(MSG_DEBUG, "%s: Session timer for STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); if (!(sta->flags & WLAN_STA_AUTH)) { if (sta->flags & WLAN_STA_GAS) { wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA " @@ -577,8 +601,8 @@ static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx) struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; - wpa_printf(MSG_DEBUG, "WNM: Session warning time reached for " MACSTR, - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "%s: WNM: Session warning time reached for " + MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); if (sta->hs20_session_info_url == NULL) return; @@ -619,7 +643,10 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) return NULL; } sta->acct_interim_interval = hapd->conf->acct_interim_interval; - accounting_sta_get_id(hapd, sta); + if (accounting_sta_get_id(hapd, sta) < 0) { + os_free(sta); + return NULL; + } if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " @@ -640,6 +667,11 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; dl_list_init(&sta->ip6addr); +#ifdef CONFIG_TAXONOMY + sta_track_claim_taxonomy_info(hapd->iface, addr, + &sta->probe_ie_taxonomy); +#endif /* CONFIG_TAXONOMY */ + return sta; } @@ -652,14 +684,16 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta) hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); ap_sta_ip6addr_del(hapd, sta); - wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver", - MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "%s: Removing STA " MACSTR " from kernel driver", + hapd->conf->iface, MAC2STR(sta->addr)); if (hostapd_drv_sta_remove(hapd, sta->addr) && sta->flags & WLAN_STA_ASSOC) { - wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR - " from kernel driver.", MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "%s: Could not remove station " MACSTR + " from kernel driver", + hapd->conf->iface, MAC2STR(sta->addr)); return -1; } + sta->added_unassoc = 0; return 0; } @@ -683,6 +717,10 @@ static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, if (!sta2) continue; + wpa_printf(MSG_DEBUG, "%s: disconnect old STA " MACSTR + " association from another BSS %s", + hapd->conf->iface, MAC2STR(sta2->addr), + bss->conf->iface); ap_sta_disconnect(bss, sta2, sta2->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } @@ -694,6 +732,8 @@ static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx) struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; + wpa_printf(MSG_DEBUG, "%s: Disassociation callback for STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); ap_sta_remove(hapd, sta); mlme_disassociate_indication(hapd, sta, sta->disassoc_reason); } @@ -717,7 +757,7 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0, ap_handle_timer, hapd, sta); accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); sta->disassoc_reason = reason; sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; @@ -733,6 +773,8 @@ static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx) struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; + wpa_printf(MSG_DEBUG, "%s: Deauthentication callback for STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); ap_sta_remove(hapd, sta); mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason); } @@ -756,7 +798,7 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, ap_handle_timer, hapd, sta); accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(sta); + ieee802_1x_free_station(hapd, sta); sta->deauth_reason = reason; sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; @@ -784,6 +826,128 @@ int ap_sta_wps_cancel(struct hostapd_data *hapd, #endif /* CONFIG_WPS */ +static int ap_sta_get_free_vlan_id(struct hostapd_data *hapd) +{ + struct hostapd_vlan *vlan; + int vlan_id = MAX_VLAN_ID + 2; + +retry: + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + if (vlan->vlan_id == vlan_id) { + vlan_id++; + goto retry; + } + } + return vlan_id; +} + + +int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, + struct vlan_description *vlan_desc) +{ + 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 */ + } else if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED && + !hapd->conf->ssid.per_sta_vif && sta->vlan_id) { + /* sta->vlan_id needs to be reset */ + } else if (!vlan_compare(vlan_desc, sta->vlan_desc)) { + return 0; /* nothing to change */ + } + + /* Now the real VLAN changed or the STA just needs its own vif */ + if (hapd->conf->ssid.per_sta_vif) { + /* Assign a new vif, always */ + /* find a free vlan_id sufficiently big */ + vlan_id = ap_sta_get_free_vlan_id(hapd); + /* Get wildcard VLAN */ + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + if (vlan->vlan_id == VLAN_ID_WILDCARD) + break; + } + if (!vlan) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "per_sta_vif missing wildcard"); + vlan_id = 0; + ret = -1; + goto done; + } + } else if (vlan_desc && vlan_desc->notempty) { + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + if (!vlan_compare(&vlan->vlan_desc, vlan_desc)) + break; + if (vlan->vlan_id == VLAN_ID_WILDCARD) + wildcard_vlan = vlan; + } + if (vlan) { + vlan_id = vlan->vlan_id; + } else if (wildcard_vlan) { + vlan = wildcard_vlan; + vlan_id = vlan_desc->untagged; + if (vlan_desc->tagged[0]) { + /* Tagged VLAN configuration */ + vlan_id = ap_sta_get_free_vlan_id(hapd); + } + } else { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "missing vlan and wildcard for vlan=%d%s", + vlan_desc->untagged, + vlan_desc->tagged[0] ? "+" : ""); + vlan_id = 0; + ret = -1; + goto done; + } + } + + if (vlan && vlan->vlan_id == VLAN_ID_WILDCARD) { + vlan = vlan_add_dynamic(hapd, vlan, vlan_id, vlan_desc); + if (vlan == NULL) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "could not add dynamic VLAN interface for vlan=%d%s", + vlan_desc ? vlan_desc->untagged : -1, + (vlan_desc && vlan_desc->tagged[0]) ? + "+" : ""); + vlan_id = 0; + ret = -1; + goto done; + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "added new dynamic VLAN interface '%s'", + vlan->ifname); + } else if (vlan && vlan->dynamic_vlan > 0) { + vlan->dynamic_vlan++; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "updated existing dynamic VLAN interface '%s'", + vlan->ifname); + } +done: + old_vlan_id = sta->vlan_id; + sta->vlan_id = vlan_id; + sta->vlan_desc = vlan ? &vlan->vlan_desc : NULL; + + if (vlan_id != old_vlan_id && old_vlan_id) + vlan_remove_dynamic(hapd, old_vlan_id); + + return ret; +} + + int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) { #ifndef CONFIG_NO_VLAN @@ -796,20 +960,11 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) if (hapd->conf->ssid.vlan[0]) iface = hapd->conf->ssid.vlan; - if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) - sta->vlan_id = 0; - else if (sta->vlan_id > 0) { - struct hostapd_vlan *wildcard_vlan = NULL; - vlan = hapd->conf->vlan; - while (vlan) { + if (sta->vlan_id > 0) { + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { if (vlan->vlan_id == sta->vlan_id) break; - if (vlan->vlan_id == VLAN_ID_WILDCARD) - wildcard_vlan = vlan; - vlan = vlan->next; } - if (!vlan) - vlan = wildcard_vlan; if (vlan) iface = vlan->ifname; } @@ -829,54 +984,13 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta) sta->vlan_id); ret = -1; goto done; - } else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) { - vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id); - if (vlan == NULL) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "could not add " - "dynamic VLAN interface for vlan_id=%d", - sta->vlan_id); - ret = -1; - goto done; - } - - iface = vlan->ifname; - if (vlan_setup_encryption_dyn(hapd, iface) != 0) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "could not " - "configure encryption for dynamic VLAN " - "interface for vlan_id=%d", - sta->vlan_id); - } - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN " - "interface '%s'", iface); - } else if (vlan && vlan->vlan_id == sta->vlan_id) { - if (vlan->dynamic_vlan > 0) { - vlan->dynamic_vlan++; - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "updated existing " - "dynamic VLAN interface '%s'", iface); - } - - /* - * Update encryption configuration for statically generated - * VLAN interface. This is only used for static WEP - * configuration for the case where hostapd did not yet know - * which keys are to be used when the interface was added. - */ - if (vlan_setup_encryption_dyn(hapd, iface) != 0) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "could not " - "configure encryption for VLAN " - "interface for vlan_id=%d", - sta->vlan_id); - } + } else if (vlan && vlan->dynamic_vlan > 0) { + vlan->dynamic_vlan++; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "updated existing dynamic VLAN interface '%s'", + iface); } /* ref counters have been increased, so mark the station */ @@ -942,6 +1056,10 @@ static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx) unsigned int timeout, sec, usec; u8 *trans_id, *nbuf; + wpa_printf(MSG_DEBUG, "%s: SA Query timer for STA " MACSTR + " (count=%d)", + hapd->conf->iface, MAC2STR(sta->addr), sta->sa_query_count); + if (sta->sa_query_count > 0 && ap_check_sa_query_timeout(hapd, sta)) return; @@ -1080,6 +1198,14 @@ void ap_sta_set_authorized(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) { + if (sta) + wpa_printf(MSG_DEBUG, "%s: %s STA " MACSTR " reason=%u", + hapd->conf->iface, __func__, MAC2STR(sta->addr), + reason); + else if (addr) + wpa_printf(MSG_DEBUG, "%s: %s addr " MACSTR " reason=%u", + hapd->conf->iface, __func__, MAC2STR(addr), + reason); if (sta == NULL && addr) sta = ap_get_sta(hapd, addr); @@ -1093,10 +1219,10 @@ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " + wpa_printf(MSG_DEBUG, "%s: %s: reschedule ap_handle_timer timeout " "for " MACSTR " (%d seconds - " "AP_MAX_INACTIVITY_AFTER_DEAUTH)", - __func__, MAC2STR(sta->addr), + hapd->conf->iface, __func__, MAC2STR(sta->addr), AP_MAX_INACTIVITY_AFTER_DEAUTH); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, @@ -1136,6 +1262,22 @@ void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta) } +void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd, + struct sta_info *sta) +{ + if (eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta) > 0) + wpa_printf(MSG_DEBUG, + "%s: Removed ap_sta_deauth_cb_timeout timeout for " + MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + if (eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta) > 0) + wpa_printf(MSG_DEBUG, + "%s: Removed ap_sta_disassoc_cb_timeout timeout for " + MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); +} + + int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) { int res; diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 420d64e5793bf..099de62d1a9ae 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -12,9 +12,11 @@ #ifdef CONFIG_MESH /* needed for mesh_plink_state enum */ #include "common/defs.h" +#include "common/wpa_common.h" #endif /* CONFIG_MESH */ #include "list.h" +#include "vlan.h" /* STA flags */ #define WLAN_STA_AUTH BIT(0) @@ -45,6 +47,20 @@ #define WLAN_SUPP_RATES_MAX 32 +struct mbo_non_pref_chan_info { + struct mbo_non_pref_chan_info *next; + u8 op_class; + u8 pref; + u8 reason_code; + u8 num_channels; + u8 channels[]; +}; + +struct pending_eapol_rx { + struct wpabuf *buf; + struct os_reltime rx_time; +}; + struct sta_info { struct sta_info *next; /* next entry in sta list */ struct sta_info *hnext; /* next entry in hash table list */ @@ -63,13 +79,22 @@ struct sta_info { enum mesh_plink_state plink_state; u16 peer_lid; u16 my_lid; + u16 peer_aid; u16 mpm_close_reason; int mpm_retries; - u8 my_nonce[32]; - u8 peer_nonce[32]; + u8 my_nonce[WPA_NONCE_LEN]; + u8 peer_nonce[WPA_NONCE_LEN]; u8 aek[32]; /* SHA256 digest length */ - u8 mtk[16]; - u8 mgtk[16]; + u8 mtk[WPA_TK_MAX_LEN]; + size_t mtk_len; + u8 mgtk_rsc[6]; + u8 mgtk_key_id; + u8 mgtk[WPA_TK_MAX_LEN]; + size_t mgtk_len; + u8 igtk_rsc[6]; + u8 igtk[WPA_TK_MAX_LEN]; + size_t igtk_len; + u16 igtk_key_id; u8 sae_auth_retry; #endif /* CONFIG_MESH */ @@ -86,6 +111,8 @@ struct sta_info { unsigned int hs20_deauth_requested:1; unsigned int session_timeout_set:1; unsigned int radius_das_match:1; + unsigned int ecsa_supported:1; + unsigned int added_unassoc:1; u16 auth_alg; @@ -100,17 +127,20 @@ struct sta_info { /* IEEE 802.1X related data */ struct eapol_state_machine *eapol_sm; - u32 acct_session_id_hi; - u32 acct_session_id_lo; + struct pending_eapol_rx *pending_eapol_rx; + + u64 acct_session_id; struct os_reltime acct_session_start; int acct_session_started; int acct_terminate_cause; /* Acct-Terminate-Cause */ int acct_interim_interval; /* Acct-Interim-Interval */ + unsigned int acct_interim_errors; - unsigned long last_rx_bytes; - unsigned long last_tx_bytes; - u32 acct_input_gigawords; /* Acct-Input-Gigawords */ - u32 acct_output_gigawords; /* Acct-Output-Gigawords */ + /* For extending 32-bit driver counters to 64-bit counters */ + u32 last_rx_bytes_hi; + u32 last_rx_bytes_lo; + u32 last_tx_bytes_hi; + u32 last_tx_bytes_lo; u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */ @@ -118,6 +148,7 @@ struct sta_info { struct rsn_preauth_interface *preauth_iface; int vlan_id; /* 0: none, >0: VID */ + struct vlan_description *vlan_desc; int vlan_id_bound; /* updated by ap_sta_bind_vlan() */ /* PSKs from RADIUS authentication server */ struct hostapd_sta_wpa_psk_short *psk; @@ -161,6 +192,7 @@ struct sta_info { #ifdef CONFIG_SAE struct sae_data *sae; + unsigned int mesh_sae_pmksa_caching:1; #endif /* CONFIG_SAE */ u32 session_timeout; /* valid only if session_timeout_set == 1 */ @@ -170,6 +202,22 @@ struct sta_info { u16 last_seq_ctrl; /* Last Authentication/(Re)Association Request/Action frame subtype */ u8 last_subtype; + +#ifdef CONFIG_MBO + u8 cell_capa; /* 0 = unknown (not an MBO STA); otherwise, + * enum mbo_cellular_capa values */ + struct mbo_non_pref_chan_info *non_pref_chan; +#endif /* CONFIG_MBO */ + + u8 *supp_op_classes; /* Supported Operating Classes element, if + * received, starting from the Length field */ + + u8 rrm_enabled_capa[5]; + +#ifdef CONFIG_TAXONOMY + struct wpabuf *probe_ie_taxonomy; + struct wpabuf *assoc_ie_taxonomy; +#endif /* CONFIG_TAXONOMY */ }; @@ -180,7 +228,7 @@ struct sta_info { * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated * after AP_DEAUTH_DELAY seconds has passed after disassociation. */ #define AP_MAX_INACTIVITY (5 * 60) -#define AP_DISASSOC_DELAY (1) +#define AP_DISASSOC_DELAY (3) #define AP_DEAUTH_DELAY (1) /* Number of seconds to keep STA entry with Authenticated flag after it has * been disassociated. */ @@ -220,6 +268,8 @@ int ap_sta_wps_cancel(struct hostapd_data *hapd, struct sta_info *sta, void *ctx); #endif /* CONFIG_WPS */ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta); +int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, + struct vlan_description *vlan_desc); 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); @@ -235,6 +285,8 @@ static inline int ap_sta_is_authorized(struct sta_info *sta) void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta); void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd, + struct sta_info *sta); int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen); diff --git a/src/ap/taxonomy.c b/src/ap/taxonomy.c new file mode 100644 index 0000000000000..cea8b726f47af --- /dev/null +++ b/src/ap/taxonomy.c @@ -0,0 +1,291 @@ +/* + * hostapd / Client taxonomy + * Copyright (c) 2015 Google, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * Parse a series of IEs, as in Probe Request or (Re)Association Request frames, + * and render them to a descriptive string. The tag number of standard options + * is written to the string, while the vendor ID and subtag are written for + * vendor options. + * + * Example strings: + * 0,1,50,45,221(00904c,51) + * 0,1,33,36,48,45,221(00904c,51),221(0050f2,2) + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/wpa_ctrl.h" +#include "hostapd.h" +#include "sta_info.h" + + +/* Copy a string with no funny schtuff allowed; only alphanumerics. */ +static void no_mischief_strncpy(char *dst, const char *src, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) { + unsigned char s = src[i]; + int is_lower = s >= 'a' && s <= 'z'; + int is_upper = s >= 'A' && s <= 'Z'; + int is_digit = s >= '0' && s <= '9'; + + if (is_lower || is_upper || is_digit) { + /* TODO: if any manufacturer uses Unicode within the + * WPS header, it will get mangled here. */ + dst[i] = s; + } else { + /* Note that even spaces will be transformed to + * underscores, so 'Nexus 7' will turn into 'Nexus_7'. + * This is deliberate, to make the string easier to + * parse. */ + dst[i] = '_'; + } + } +} + + +static int get_wps_name(char *name, size_t name_len, + const u8 *data, size_t data_len) +{ + /* Inside the WPS IE are a series of attributes, using two byte IDs + * and two byte lengths. We're looking for the model name, if + * present. */ + while (data_len >= 4) { + u16 id, elen; + + id = WPA_GET_BE16(data); + elen = WPA_GET_BE16(data + 2); + data += 4; + data_len -= 4; + + if (elen > data_len) + return 0; + + if (id == 0x1023) { + /* Model name, like 'Nexus 7' */ + size_t n = (elen < name_len) ? elen : name_len; + no_mischief_strncpy(name, (const char *) data, n); + return n; + } + + data += elen; + data_len -= elen; + } + + return 0; +} + + +static void ie_to_string(char *fstr, size_t fstr_len, const struct wpabuf *ies) +{ + char *fpos = fstr; + char *fend = fstr + fstr_len; + char htcap[7 + 4 + 1]; /* ",htcap:" + %04hx + trailing NUL */ + char htagg[7 + 2 + 1]; /* ",htagg:" + %02hx + trailing NUL */ + char htmcs[7 + 8 + 1]; /* ",htmcs:" + %08x + trailing NUL */ + char vhtcap[8 + 8 + 1]; /* ",vhtcap:" + %08x + trailing NUL */ + char vhtrxmcs[10 + 8 + 1]; /* ",vhtrxmcs:" + %08x + trailing NUL */ + char vhttxmcs[10 + 8 + 1]; /* ",vhttxmcs:" + %08x + trailing NUL */ +#define MAX_EXTCAP 254 + char extcap[8 + 2 * MAX_EXTCAP + 1]; /* ",extcap:" + hex + trailing NUL + */ + char txpow[7 + 4 + 1]; /* ",txpow:" + %04hx + trailing NUL */ +#define WPS_NAME_LEN 32 + char wps[WPS_NAME_LEN + 5 + 1]; /* room to prepend ",wps:" + trailing + * NUL */ + int num = 0; + const u8 *ie; + size_t ie_len; + int ret; + + os_memset(htcap, 0, sizeof(htcap)); + os_memset(htagg, 0, sizeof(htagg)); + os_memset(htmcs, 0, sizeof(htmcs)); + os_memset(vhtcap, 0, sizeof(vhtcap)); + os_memset(vhtrxmcs, 0, sizeof(vhtrxmcs)); + os_memset(vhttxmcs, 0, sizeof(vhttxmcs)); + os_memset(extcap, 0, sizeof(extcap)); + os_memset(txpow, 0, sizeof(txpow)); + os_memset(wps, 0, sizeof(wps)); + *fpos = '\0'; + + if (!ies) + return; + ie = wpabuf_head(ies); + ie_len = wpabuf_len(ies); + + while (ie_len >= 2) { + u8 id, elen; + char *sep = (num++ == 0) ? "" : ","; + + id = *ie++; + elen = *ie++; + ie_len -= 2; + + if (elen > ie_len) + break; + + if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4) { + /* Vendor specific */ + if (WPA_GET_BE32(ie) == WPS_IE_VENDOR_TYPE) { + /* WPS */ + char model_name[WPS_NAME_LEN + 1]; + const u8 *data = &ie[4]; + size_t data_len = elen - 4; + + os_memset(model_name, 0, sizeof(model_name)); + if (get_wps_name(model_name, WPS_NAME_LEN, data, + data_len)) { + os_snprintf(wps, sizeof(wps), + ",wps:%s", model_name); + } + } + + ret = os_snprintf(fpos, fend - fpos, + "%s%d(%02x%02x%02x,%d)", + sep, id, ie[0], ie[1], ie[2], ie[3]); + } else { + if (id == WLAN_EID_HT_CAP && elen >= 2) { + /* HT Capabilities (802.11n) */ + os_snprintf(htcap, sizeof(htcap), + ",htcap:%04hx", + WPA_GET_LE16(ie)); + } + if (id == WLAN_EID_HT_CAP && elen >= 3) { + /* HT Capabilities (802.11n), A-MPDU information + */ + os_snprintf(htagg, sizeof(htagg), + ",htagg:%02hx", (u16) ie[2]); + } + if (id == WLAN_EID_HT_CAP && elen >= 7) { + /* HT Capabilities (802.11n), MCS information */ + os_snprintf(htmcs, sizeof(htmcs), + ",htmcs:%08hx", + (u16) WPA_GET_LE32(ie + 3)); + } + if (id == WLAN_EID_VHT_CAP && elen >= 4) { + /* VHT Capabilities (802.11ac) */ + os_snprintf(vhtcap, sizeof(vhtcap), + ",vhtcap:%08x", + WPA_GET_LE32(ie)); + } + if (id == WLAN_EID_VHT_CAP && elen >= 8) { + /* VHT Capabilities (802.11ac), RX MCS + * information */ + os_snprintf(vhtrxmcs, sizeof(vhtrxmcs), + ",vhtrxmcs:%08x", + WPA_GET_LE32(ie + 4)); + } + if (id == WLAN_EID_VHT_CAP && elen >= 12) { + /* VHT Capabilities (802.11ac), TX MCS + * information */ + os_snprintf(vhttxmcs, sizeof(vhttxmcs), + ",vhttxmcs:%08x", + WPA_GET_LE32(ie + 8)); + } + if (id == WLAN_EID_EXT_CAPAB) { + /* Extended Capabilities */ + int i; + int len = (elen < MAX_EXTCAP) ? elen : + MAX_EXTCAP; + char *p = extcap; + + p += os_snprintf(extcap, sizeof(extcap), + ",extcap:"); + for (i = 0; i < len; i++) { + int lim; + + lim = sizeof(extcap) - + os_strlen(extcap); + if (lim <= 0) + break; + p += os_snprintf(p, lim, "%02x", + *(ie + i)); + } + } + if (id == WLAN_EID_PWR_CAPABILITY && elen == 2) { + /* TX Power */ + os_snprintf(txpow, sizeof(txpow), + ",txpow:%04hx", + WPA_GET_LE16(ie)); + } + + ret = os_snprintf(fpos, fend - fpos, "%s%d", sep, id); + } + if (os_snprintf_error(fend - fpos, ret)) + goto fail; + fpos += ret; + + ie += elen; + ie_len -= elen; + } + + ret = os_snprintf(fpos, fend - fpos, "%s%s%s%s%s%s%s%s%s", + htcap, htagg, htmcs, vhtcap, vhtrxmcs, vhttxmcs, + txpow, extcap, wps); + if (os_snprintf_error(fend - fpos, ret)) { + fail: + fstr[0] = '\0'; + } +} + + +int retrieve_sta_taxonomy(const struct hostapd_data *hapd, + struct sta_info *sta, char *buf, size_t buflen) +{ + int ret; + char *pos, *end; + + if (!sta->probe_ie_taxonomy || !sta->assoc_ie_taxonomy) + return 0; + + ret = os_snprintf(buf, buflen, "wifi4|probe:"); + if (os_snprintf_error(buflen, ret)) + return 0; + pos = buf + ret; + end = buf + buflen; + + ie_to_string(pos, end - pos, sta->probe_ie_taxonomy); + pos = os_strchr(pos, '\0'); + if (pos >= end) + return 0; + ret = os_snprintf(pos, end - pos, "|assoc:"); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + ie_to_string(pos, end - pos, sta->assoc_ie_taxonomy); + pos = os_strchr(pos, '\0'); + return pos - buf; +} + + +void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *ie, size_t ie_len) +{ + wpabuf_free(sta->probe_ie_taxonomy); + sta->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len); +} + + +void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd, + struct hostapd_sta_info *info, + const u8 *ie, size_t ie_len) +{ + wpabuf_free(info->probe_ie_taxonomy); + info->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len); +} + + +void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *ie, size_t ie_len) +{ + wpabuf_free(sta->assoc_ie_taxonomy); + sta->assoc_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len); +} diff --git a/src/ap/taxonomy.h b/src/ap/taxonomy.h new file mode 100644 index 0000000000000..80f245c77c82c --- /dev/null +++ b/src/ap/taxonomy.h @@ -0,0 +1,24 @@ +/* + * hostapd / Station client taxonomy + * Copyright (c) 2015 Google, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TAXONOMY_H +#define TAXONOMY_H + +void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *ie, size_t ie_len); +void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd, + struct hostapd_sta_info *sta, + const u8 *ie, size_t ie_len); +void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *ie, size_t ie_len); +int retrieve_sta_taxonomy(const struct hostapd_data *hapd, + struct sta_info *sta, char *buf, size_t buflen); + +#endif /* TAXONOMY_H */ diff --git a/src/ap/vlan.c b/src/ap/vlan.c new file mode 100644 index 0000000000000..b6f6bb1afe055 --- /dev/null +++ b/src/ap/vlan.c @@ -0,0 +1,34 @@ +/* + * hostapd / VLAN definition + * Copyright (c) 2016, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "ap/vlan.h" + +/* compare the two arguments, NULL is treated as empty + * return zero iff they are equal + */ +int vlan_compare(struct vlan_description *a, struct vlan_description *b) +{ + int i; + const int a_empty = !a || !a->notempty; + const int b_empty = !b || !b->notempty; + + if (a_empty && b_empty) + return 0; + if (a_empty || b_empty) + return 1; + if (a->untagged != b->untagged) + return 1; + for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) { + if (a->tagged[i] != b->tagged[i]) + return 1; + } + return 0; +} diff --git a/src/ap/vlan.h b/src/ap/vlan.h new file mode 100644 index 0000000000000..af84929decdc4 --- /dev/null +++ b/src/ap/vlan.h @@ -0,0 +1,30 @@ +/* + * hostapd / VLAN definition + * Copyright (c) 2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef VLAN_H +#define VLAN_H + +#define MAX_NUM_TAGGED_VLAN 32 + +struct vlan_description { + int notempty; /* 0 : no vlan information present, 1: else */ + int untagged; /* >0 802.1q vid */ + int tagged[MAX_NUM_TAGGED_VLAN]; /* first k items, ascending order */ +}; + +#ifndef CONFIG_NO_VLAN +int vlan_compare(struct vlan_description *a, struct vlan_description *b); +#else /* CONFIG_NO_VLAN */ +static inline int +vlan_compare(struct vlan_description *a, struct vlan_description *b) +{ + return 0; +} +#endif /* CONFIG_NO_VLAN */ + +#endif /* VLAN_H */ diff --git a/src/ap/vlan_full.c b/src/ap/vlan_full.c new file mode 100644 index 0000000000000..aa42335b96a19 --- /dev/null +++ b/src/ap/vlan_full.c @@ -0,0 +1,752 @@ +/* + * hostapd / VLAN initialization - full dynamic VLAN + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include <net/if.h> +/* Avoid conflicts due to NetBSD net/if.h if_type define with driver.h */ +#undef if_type +#include <sys/ioctl.h> + +#include "utils/common.h" +#include "drivers/priv_netlink.h" +#include "common/linux_bridge.h" +#include "common/linux_vlan.h" +#include "utils/eloop.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "wpa_auth.h" +#include "vlan_init.h" +#include "vlan_util.h" + + +struct full_dynamic_vlan { + int s; /* socket on which to listen for new/removed interfaces. */ +}; + +#define DVLAN_CLEAN_BR 0x1 +#define DVLAN_CLEAN_VLAN 0x2 +#define DVLAN_CLEAN_VLAN_PORT 0x4 + +struct dynamic_iface { + char ifname[IFNAMSIZ + 1]; + int usage; + int clean; + struct dynamic_iface *next; +}; + + +/* Increment ref counter for ifname and add clean flag. + * If not in list, add it only if some flags are given. + */ +static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname, + int clean) +{ + struct dynamic_iface *next, **dynamic_ifaces; + struct hapd_interfaces *interfaces; + + interfaces = hapd->iface->interfaces; + dynamic_ifaces = &interfaces->vlan_priv; + + for (next = *dynamic_ifaces; next; next = next->next) { + if (os_strcmp(ifname, next->ifname) == 0) + break; + } + + if (next) { + next->usage++; + next->clean |= clean; + return; + } + + if (!clean) + return; + + next = os_zalloc(sizeof(*next)); + if (!next) + return; + os_strlcpy(next->ifname, ifname, sizeof(next->ifname)); + next->usage = 1; + next->clean = clean; + next->next = *dynamic_ifaces; + *dynamic_ifaces = next; +} + + +/* Decrement reference counter for given ifname. + * Return clean flag iff reference counter was decreased to zero, else zero + */ +static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname) +{ + struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces; + struct hapd_interfaces *interfaces; + int clean; + + interfaces = hapd->iface->interfaces; + dynamic_ifaces = &interfaces->vlan_priv; + + for (next = *dynamic_ifaces; next; next = next->next) { + if (os_strcmp(ifname, next->ifname) == 0) + break; + prev = next; + } + + if (!next) + return 0; + + next->usage--; + if (next->usage) + return 0; + + if (prev) + prev->next = next->next; + else + *dynamic_ifaces = next->next; + clean = next->clean; + os_free(next); + + return clean; +} + + +static int ifconfig_down(const char *if_name) +{ + wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name); + return ifconfig_helper(if_name, 0); +} + + +/* This value should be 256 ONLY. If it is something else, then hostapd + * might crash!, as this value has been hard-coded in 2.4.x kernel + * bridging code. + */ +#define MAX_BR_PORTS 256 + +static int br_delif(const char *br_name, const char *if_name) +{ + int fd; + struct ifreq ifr; + unsigned long args[2]; + int if_index; + + wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + if_index = if_nametoindex(if_name); + + if (if_index == 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " + "interface index for '%s'", + __func__, if_name); + close(fd); + return -1; + } + + args[0] = BRCTL_DEL_IF; + args[1] = if_index; + + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (void *) args; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { + /* No error if interface already removed. */ + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," + "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: " + "%s", __func__, br_name, if_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add interface 'if_name' to the bridge 'br_name' + + returns -1 on error + returns 1 if the interface is already part of the bridge + returns 0 otherwise +*/ +static int br_addif(const char *br_name, const char *if_name) +{ + int fd; + struct ifreq ifr; + unsigned long args[2]; + int if_index; + + wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + if_index = if_nametoindex(if_name); + + if (if_index == 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " + "interface index for '%s'", + __func__, if_name); + close(fd); + return -1; + } + + args[0] = BRCTL_ADD_IF; + args[1] = if_index; + + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (void *) args; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + if (errno == EBUSY) { + /* The interface is already added. */ + close(fd); + return 1; + } + + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," + "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: " + "%s", __func__, br_name, if_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static int br_delbr(const char *br_name) +{ + int fd; + unsigned long arg[2]; + + wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + arg[0] = BRCTL_DEL_BRIDGE; + arg[1] = (unsigned long) br_name; + + if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) { + /* No error if bridge already removed. */ + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for " + "%s: %s", __func__, br_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add a bridge with the name 'br_name'. + + returns -1 on error + returns 1 if the bridge already exists + returns 0 otherwise +*/ +static int br_addbr(const char *br_name) +{ + int fd; + unsigned long arg[4]; + struct ifreq ifr; + + wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + arg[0] = BRCTL_ADD_BRIDGE; + arg[1] = (unsigned long) br_name; + + if (ioctl(fd, SIOCGIFBR, arg) < 0) { + if (errno == EEXIST) { + /* The bridge is already added. */ + close(fd); + return 1; + } else { + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE " + "failed for %s: %s", + __func__, br_name, strerror(errno)); + close(fd); + return -1; + } + } + + /* Decrease forwarding delay to avoid EAPOL timeouts. */ + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ); + arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY; + arg[1] = 1; + arg[2] = 0; + arg[3] = 0; + ifr.ifr_data = (char *) &arg; + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: " + "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for " + "%s: %s", __func__, br_name, strerror(errno)); + /* Continue anyway */ + } + + close(fd); + return 0; +} + + +static int br_getnumports(const char *br_name) +{ + int fd; + int i; + int port_cnt = 0; + unsigned long arg[4]; + int ifindices[MAX_BR_PORTS]; + struct ifreq ifr; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + arg[0] = BRCTL_GET_PORT_LIST; + arg[1] = (unsigned long) ifindices; + arg[2] = MAX_BR_PORTS; + arg[3] = 0; + + os_memset(ifindices, 0, sizeof(ifindices)); + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (void *) arg; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST " + "failed for %s: %s", + __func__, br_name, strerror(errno)); + close(fd); + return -1; + } + + for (i = 1; i < MAX_BR_PORTS; i++) { + if (ifindices[i] > 0) { + port_cnt++; + } + } + + close(fd); + return port_cnt; +} + + +static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface, + const char *br_name, int vid, + struct hostapd_data *hapd) +{ + char vlan_ifname[IFNAMSIZ]; + int clean; + + if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) + os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", + tagged_interface, vid); + else + os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid); + + clean = 0; + ifconfig_up(tagged_interface); + if (!vlan_add(tagged_interface, vid, vlan_ifname)) + clean |= DVLAN_CLEAN_VLAN; + + if (!br_addif(br_name, vlan_ifname)) + clean |= DVLAN_CLEAN_VLAN_PORT; + + dyn_iface_get(hapd, vlan_ifname, clean); + + ifconfig_up(vlan_ifname); +} + + +static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, 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); + } else if (tagged_interface) { + os_snprintf(br_name, IFNAMSIZ, "br%s.%d", + tagged_interface, vid); + } else { + os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid); + } +} + + +static void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd, + int vid) +{ + char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int vlan_naming = hapd->conf->ssid.vlan_naming; + + dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR); + + ifconfig_up(br_name); + + if (tagged_interface) + vlan_newlink_tagged(vlan_naming, tagged_interface, br_name, + vid, hapd); +} + + +void vlan_newlink(const char *ifname, struct hostapd_data *hapd) +{ + char br_name[IFNAMSIZ]; + struct hostapd_vlan *vlan; + int untagged, *tagged, i, notempty; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); + + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + if (vlan->configured || + os_strcmp(ifname, vlan->ifname) != 0) + continue; + break; + } + if (!vlan) + return; + + vlan->configured = 1; + + notempty = vlan->vlan_desc.notempty; + untagged = vlan->vlan_desc.untagged; + tagged = vlan->vlan_desc.tagged; + + if (!notempty) { + /* Non-VLAN STA */ + if (hapd->conf->bridge[0] && + !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_get_bridge(br_name, hapd, untagged); + + if (!br_addif(br_name, ifname)) + vlan->clean |= DVLAN_CLEAN_WLAN_PORT; + } + + for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) { + if (tagged[i] == untagged || + 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_get_bridge(br_name, hapd, tagged[i]); + vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE, + ifname, br_name, tagged[i], hapd); + } + + ifconfig_up(ifname); +} + + +static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface, + const char *br_name, int vid, + struct hostapd_data *hapd) +{ + char vlan_ifname[IFNAMSIZ]; + int clean; + + if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) + os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", + tagged_interface, vid); + else + os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid); + + clean = dyn_iface_put(hapd, vlan_ifname); + + if (clean & DVLAN_CLEAN_VLAN_PORT) + br_delif(br_name, vlan_ifname); + + if (clean & DVLAN_CLEAN_VLAN) { + ifconfig_down(vlan_ifname); + vlan_rem(vlan_ifname); + } +} + + +static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd, + int vid) +{ + int clean; + char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int vlan_naming = hapd->conf->ssid.vlan_naming; + + if (tagged_interface) + vlan_dellink_tagged(vlan_naming, tagged_interface, br_name, + vid, hapd); + + clean = dyn_iface_put(hapd, br_name); + if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) { + ifconfig_down(br_name); + br_delbr(br_name); + } +} + + +void vlan_dellink(const char *ifname, struct hostapd_data *hapd) +{ + struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); + + first = prev = vlan; + + while (vlan) { + if (os_strcmp(ifname, vlan->ifname) != 0) { + prev = vlan; + vlan = vlan->next; + continue; + } + break; + } + if (!vlan) + return; + + if (vlan->configured) { + int notempty = vlan->vlan_desc.notempty; + int untagged = vlan->vlan_desc.untagged; + int *tagged = vlan->vlan_desc.tagged; + char br_name[IFNAMSIZ]; + int i; + + for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) { + if (tagged[i] == untagged || + 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_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE, + ifname, br_name, tagged[i], hapd); + vlan_put_bridge(br_name, hapd, tagged[i]); + } + + if (!notempty) { + /* Non-VLAN STA */ + if (hapd->conf->bridge[0] && + (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); + + if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) + br_delif(br_name, vlan->ifname); + + vlan_put_bridge(br_name, hapd, untagged); + } + } + + /* + * Ensure this VLAN interface is actually removed even if + * NEWLINK message is only received later. + */ + if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan)) + wpa_printf(MSG_ERROR, + "VLAN: Could not remove VLAN iface: %s: %s", + vlan->ifname, strerror(errno)); + + if (vlan == first) + hapd->conf->vlan = vlan->next; + else + prev->next = vlan->next; + + os_free(vlan); +} + + +static void +vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, + struct hostapd_data *hapd) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr *attr; + char ifname[IFNAMSIZ + 1]; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + os_memset(ifname, 0, sizeof(ifname)); + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + int n = attr->rta_len - rta_len; + if (n < 0) + break; + + if ((size_t) n >= sizeof(ifname)) + n = sizeof(ifname) - 1; + os_memcpy(ifname, ((char *) attr) + rta_len, n); + + } + + attr = RTA_NEXT(attr, attrlen); + } + + if (!ifname[0]) + return; + if (del && if_nametoindex(ifname)) { + /* interface still exists, race condition -> + * iface has just been recreated */ + return; + } + + wpa_printf(MSG_DEBUG, + "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", + del ? "DEL" : "NEW", + ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + + if (del) + vlan_dellink(ifname, hapd); + else + vlan_newlink(ifname, hapd); +} + + +static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + struct hostapd_data *hapd = eloop_ctx; + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s", + __func__, strerror(errno)); + return; + } + + h = (struct nlmsghdr *) buf; + while (NLMSG_OK(h, left)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink " + "message: len=%d left=%d plen=%d", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + vlan_read_ifnames(h, plen, 0, hapd); + break; + case RTM_DELLINK: + vlan_read_ifnames(h, plen, 1, hapd); + break; + } + + h = NLMSG_NEXT(h, left); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of " + "netlink message", __func__, left); + } +} + + +struct full_dynamic_vlan * +full_dynamic_vlan_init(struct hostapd_data *hapd) +{ + struct sockaddr_nl local; + struct full_dynamic_vlan *priv; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + + vlan_set_name_type(hapd->conf->ssid.vlan_naming == + DYNAMIC_VLAN_NAMING_WITH_DEVICE ? + VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD : + VLAN_NAME_TYPE_PLUS_VID_NO_PAD); + + priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (priv->s < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW," + "NETLINK_ROUTE) failed: %s", + __func__, strerror(errno)); + os_free(priv); + return NULL; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s", + __func__, strerror(errno)); + close(priv->s); + os_free(priv); + return NULL; + } + + if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL)) + { + close(priv->s); + os_free(priv); + return NULL; + } + + return priv; +} + + +void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) +{ + if (priv == NULL) + return; + eloop_unregister_read_sock(priv->s); + close(priv->s); + os_free(priv); +} diff --git a/src/ap/vlan_ifconfig.c b/src/ap/vlan_ifconfig.c new file mode 100644 index 0000000000000..ef953a5c4c95a --- /dev/null +++ b/src/ap/vlan_ifconfig.c @@ -0,0 +1,69 @@ +/* + * hostapd / VLAN ifconfig helpers + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include <net/if.h> +#include <sys/ioctl.h> + +#include "utils/common.h" +#include "vlan_util.h" + + +int ifconfig_helper(const char *if_name, int up) +{ + int fd; + struct ifreq ifr; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ); + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed " + "for interface %s: %s", + __func__, if_name, strerror(errno)); + close(fd); + return -1; + } + + if (up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed " + "for interface %s (up=%d): %s", + __func__, if_name, up, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +int ifconfig_up(const char *if_name) +{ + wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name); + return ifconfig_helper(if_name, 1); +} + + +int iface_exists(const char *ifname) +{ + return if_nametoindex(ifname); +} diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c index fd1c8ddacee62..31e4fc6b396a1 100644 --- a/src/ap/vlan_init.c +++ b/src/ap/vlan_init.c @@ -9,902 +9,72 @@ */ #include "utils/includes.h" -#ifdef CONFIG_FULL_DYNAMIC_VLAN -#include <net/if.h> -#include <sys/ioctl.h> -#include <linux/sockios.h> -#include <linux/if_vlan.h> -#include <linux/if_bridge.h> -#endif /* CONFIG_FULL_DYNAMIC_VLAN */ #include "utils/common.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "wpa_auth.h" #include "vlan_init.h" #include "vlan_util.h" -#ifdef CONFIG_FULL_DYNAMIC_VLAN - -#include "drivers/priv_netlink.h" -#include "utils/eloop.h" - - -struct full_dynamic_vlan { - int s; /* socket on which to listen for new/removed interfaces. */ -}; - -#define DVLAN_CLEAN_BR 0x1 -#define DVLAN_CLEAN_VLAN 0x2 -#define DVLAN_CLEAN_VLAN_PORT 0x4 - -struct dynamic_iface { - char ifname[IFNAMSIZ + 1]; - int usage; - int clean; - struct dynamic_iface *next; -}; - - -/* Increment ref counter for ifname and add clean flag. - * If not in list, add it only if some flags are given. - */ -static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname, - int clean) -{ - struct dynamic_iface *next, **dynamic_ifaces; - struct hapd_interfaces *interfaces; - - interfaces = hapd->iface->interfaces; - dynamic_ifaces = &interfaces->vlan_priv; - - for (next = *dynamic_ifaces; next; next = next->next) { - if (os_strcmp(ifname, next->ifname) == 0) - break; - } - - if (next) { - next->usage++; - next->clean |= clean; - return; - } - - if (!clean) - return; - - next = os_zalloc(sizeof(*next)); - if (!next) - return; - os_strlcpy(next->ifname, ifname, sizeof(next->ifname)); - next->usage = 1; - next->clean = clean; - next->next = *dynamic_ifaces; - *dynamic_ifaces = next; -} - - -/* Decrement reference counter for given ifname. - * Return clean flag iff reference counter was decreased to zero, else zero - */ -static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname) -{ - struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces; - struct hapd_interfaces *interfaces; - int clean; - - interfaces = hapd->iface->interfaces; - dynamic_ifaces = &interfaces->vlan_priv; - - for (next = *dynamic_ifaces; next; next = next->next) { - if (os_strcmp(ifname, next->ifname) == 0) - break; - prev = next; - } - - if (!next) - return 0; - - next->usage--; - if (next->usage) - return 0; - - if (prev) - prev->next = next->next; - else - *dynamic_ifaces = next->next; - clean = next->clean; - os_free(next); - - return clean; -} - - -static int ifconfig_helper(const char *if_name, int up) -{ - int fd; - struct ifreq ifr; - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ); - - if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed " - "for interface %s: %s", - __func__, if_name, strerror(errno)); - close(fd); - return -1; - } - - if (up) - ifr.ifr_flags |= IFF_UP; - else - ifr.ifr_flags &= ~IFF_UP; - - if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed " - "for interface %s (up=%d): %s", - __func__, if_name, up, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - - -static int ifconfig_up(const char *if_name) -{ - wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name); - return ifconfig_helper(if_name, 1); -} - - -static int ifconfig_down(const char *if_name) -{ - wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name); - return ifconfig_helper(if_name, 0); -} - - -/* - * These are only available in recent linux headers (without the leading - * underscore). - */ -#define _GET_VLAN_REALDEV_NAME_CMD 8 -#define _GET_VLAN_VID_CMD 9 - -/* This value should be 256 ONLY. If it is something else, then hostapd - * might crash!, as this value has been hard-coded in 2.4.x kernel - * bridging code. - */ -#define MAX_BR_PORTS 256 - -static int br_delif(const char *br_name, const char *if_name) -{ - int fd; - struct ifreq ifr; - unsigned long args[2]; - int if_index; - - wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name); - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - if_index = if_nametoindex(if_name); - - if (if_index == 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " - "interface index for '%s'", - __func__, if_name); - close(fd); - return -1; - } - - args[0] = BRCTL_DEL_IF; - args[1] = if_index; - - os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); - ifr.ifr_data = (__caddr_t) args; - - if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { - /* No error if interface already removed. */ - wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," - "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: " - "%s", __func__, br_name, if_name, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - - -/* - Add interface 'if_name' to the bridge 'br_name' - - returns -1 on error - returns 1 if the interface is already part of the bridge - returns 0 otherwise -*/ -static int br_addif(const char *br_name, const char *if_name) -{ - int fd; - struct ifreq ifr; - unsigned long args[2]; - int if_index; - - wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name); - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - if_index = if_nametoindex(if_name); - - if (if_index == 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " - "interface index for '%s'", - __func__, if_name); - close(fd); - return -1; - } - - args[0] = BRCTL_ADD_IF; - args[1] = if_index; - - os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); - ifr.ifr_data = (__caddr_t) args; - - if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { - if (errno == EBUSY) { - /* The interface is already added. */ - close(fd); - return 1; - } - - wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," - "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: " - "%s", __func__, br_name, if_name, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - - -static int br_delbr(const char *br_name) -{ - int fd; - unsigned long arg[2]; - - wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name); - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - arg[0] = BRCTL_DEL_BRIDGE; - arg[1] = (unsigned long) br_name; - - if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) { - /* No error if bridge already removed. */ - wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for " - "%s: %s", __func__, br_name, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - - -/* - Add a bridge with the name 'br_name'. - - returns -1 on error - returns 1 if the bridge already exists - returns 0 otherwise -*/ -static int br_addbr(const char *br_name) -{ - int fd; - unsigned long arg[4]; - struct ifreq ifr; - - wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name); - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - arg[0] = BRCTL_ADD_BRIDGE; - arg[1] = (unsigned long) br_name; - - if (ioctl(fd, SIOCGIFBR, arg) < 0) { - if (errno == EEXIST) { - /* The bridge is already added. */ - close(fd); - return 1; - } else { - wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE " - "failed for %s: %s", - __func__, br_name, strerror(errno)); - close(fd); - return -1; - } - } - - /* Decrease forwarding delay to avoid EAPOL timeouts. */ - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ); - arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY; - arg[1] = 1; - arg[2] = 0; - arg[3] = 0; - ifr.ifr_data = (char *) &arg; - if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: " - "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for " - "%s: %s", __func__, br_name, strerror(errno)); - /* Continue anyway */ - } - - close(fd); - return 0; -} - - -static int br_getnumports(const char *br_name) -{ - int fd; - int i; - int port_cnt = 0; - unsigned long arg[4]; - int ifindices[MAX_BR_PORTS]; - struct ifreq ifr; - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - arg[0] = BRCTL_GET_PORT_LIST; - arg[1] = (unsigned long) ifindices; - arg[2] = MAX_BR_PORTS; - arg[3] = 0; - - os_memset(ifindices, 0, sizeof(ifindices)); - os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); - ifr.ifr_data = (__caddr_t) arg; - - if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST " - "failed for %s: %s", - __func__, br_name, strerror(errno)); - close(fd); - return -1; - } - - for (i = 1; i < MAX_BR_PORTS; i++) { - if (ifindices[i] > 0) { - port_cnt++; - } - } - - close(fd); - return port_cnt; -} - - -#ifndef CONFIG_VLAN_NETLINK - -int vlan_rem(const char *if_name) -{ - int fd; - struct vlan_ioctl_args if_request; - - wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name); - if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { - wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", - if_name); - return -1; - } - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - os_memset(&if_request, 0, sizeof(if_request)); - - os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); - if_request.cmd = DEL_VLAN_CMD; - - if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: " - "%s", __func__, if_name, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - - -/* - Add a vlan interface with VLAN ID 'vid' and tagged interface - 'if_name'. - - returns -1 on error - returns 1 if the interface already exists - returns 0 otherwise -*/ -int vlan_add(const char *if_name, int vid, const char *vlan_if_name) -{ - int fd; - struct vlan_ioctl_args if_request; - - wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)", - if_name, vid); - ifconfig_up(if_name); - - if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { - wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", - if_name); - return -1; - } - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); - return -1; - } - - os_memset(&if_request, 0, sizeof(if_request)); - - /* Determine if a suitable vlan device already exists. */ - - os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d", - vid); - - if_request.cmd = _GET_VLAN_VID_CMD; - - if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) { - - if (if_request.u.VID == vid) { - if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD; - - if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && - os_strncmp(if_request.u.device2, if_name, - sizeof(if_request.u.device2)) == 0) { - close(fd); - wpa_printf(MSG_DEBUG, "VLAN: vlan_add: " - "if_name %s exists already", - if_request.device1); - return 1; - } - } - } - - /* A suitable vlan device does not already exist, add one. */ - - os_memset(&if_request, 0, sizeof(if_request)); - os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); - if_request.u.VID = vid; - if_request.cmd = ADD_VLAN_CMD; - - if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: " - "%s", - __func__, if_request.device1, strerror(errno)); - close(fd); - return -1; - } - - close(fd); - return 0; -} - - -static int vlan_set_name_type(unsigned int name_type) +static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan, + int existsok) { - int fd; - struct vlan_ioctl_args if_request; + int ret, i; - wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)", - name_type); - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " - "failed: %s", __func__, strerror(errno)); + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (!hapd->conf->ssid.wep.key[i]) + continue; + wpa_printf(MSG_ERROR, + "VLAN: Refusing to set up VLAN iface %s with WEP", + vlan->ifname); return -1; } - os_memset(&if_request, 0, sizeof(if_request)); - - if_request.u.name_type = name_type; - if_request.cmd = SET_VLAN_NAME_TYPE_CMD; - if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD " - "name_type=%u failed: %s", - __func__, name_type, strerror(errno)); - close(fd); + if (!iface_exists(vlan->ifname)) + ret = hostapd_vlan_if_add(hapd, vlan->ifname); + else if (!existsok) return -1; - } - - close(fd); - return 0; -} - -#endif /* CONFIG_VLAN_NETLINK */ - - -static void vlan_newlink(char *ifname, struct hostapd_data *hapd) -{ - char vlan_ifname[IFNAMSIZ]; - char br_name[IFNAMSIZ]; - struct hostapd_vlan *vlan = hapd->conf->vlan; - char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; - int vlan_naming = hapd->conf->ssid.vlan_naming; - int clean; - - wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); - - while (vlan) { - if (os_strcmp(ifname, vlan->ifname) == 0 && !vlan->configured) { - vlan->configured = 1; - - if (hapd->conf->vlan_bridge[0]) { - os_snprintf(br_name, sizeof(br_name), "%s%d", - hapd->conf->vlan_bridge, - vlan->vlan_id); - } else if (tagged_interface) { - os_snprintf(br_name, sizeof(br_name), - "br%s.%d", tagged_interface, - vlan->vlan_id); - } else { - os_snprintf(br_name, sizeof(br_name), - "brvlan%d", vlan->vlan_id); - } - - dyn_iface_get(hapd, br_name, - br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR); - - ifconfig_up(br_name); - - if (tagged_interface) { - if (vlan_naming == - DYNAMIC_VLAN_NAMING_WITH_DEVICE) - os_snprintf(vlan_ifname, - sizeof(vlan_ifname), - "%s.%d", tagged_interface, - vlan->vlan_id); - else - os_snprintf(vlan_ifname, - sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); - - clean = 0; - ifconfig_up(tagged_interface); - if (!vlan_add(tagged_interface, vlan->vlan_id, - vlan_ifname)) - clean |= DVLAN_CLEAN_VLAN; - - if (!br_addif(br_name, vlan_ifname)) - clean |= DVLAN_CLEAN_VLAN_PORT; - - dyn_iface_get(hapd, vlan_ifname, clean); - - ifconfig_up(vlan_ifname); - } - - if (!br_addif(br_name, ifname)) - vlan->clean |= DVLAN_CLEAN_WLAN_PORT; - - ifconfig_up(ifname); - - break; - } - vlan = vlan->next; - } -} - - -static void vlan_dellink(char *ifname, struct hostapd_data *hapd) -{ - char vlan_ifname[IFNAMSIZ]; - char br_name[IFNAMSIZ]; - struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; - char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; - int vlan_naming = hapd->conf->ssid.vlan_naming; - int clean; - - wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); - - first = prev = vlan; - - while (vlan) { - if (os_strcmp(ifname, vlan->ifname) == 0 && - vlan->configured) { - if (hapd->conf->vlan_bridge[0]) { - os_snprintf(br_name, sizeof(br_name), "%s%d", - hapd->conf->vlan_bridge, - vlan->vlan_id); - } else if (tagged_interface) { - os_snprintf(br_name, sizeof(br_name), - "br%s.%d", tagged_interface, - vlan->vlan_id); - } else { - os_snprintf(br_name, sizeof(br_name), - "brvlan%d", vlan->vlan_id); - } - - if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) - br_delif(br_name, vlan->ifname); - - if (tagged_interface) { - if (vlan_naming == - DYNAMIC_VLAN_NAMING_WITH_DEVICE) - os_snprintf(vlan_ifname, - sizeof(vlan_ifname), - "%s.%d", tagged_interface, - vlan->vlan_id); - else - os_snprintf(vlan_ifname, - sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); - - clean = dyn_iface_put(hapd, vlan_ifname); - - if (clean & DVLAN_CLEAN_VLAN_PORT) - br_delif(br_name, vlan_ifname); - - if (clean & DVLAN_CLEAN_VLAN) { - ifconfig_down(vlan_ifname); - vlan_rem(vlan_ifname); - } - } - - clean = dyn_iface_put(hapd, br_name); - if ((clean & DVLAN_CLEAN_BR) && - br_getnumports(br_name) == 0) { - ifconfig_down(br_name); - br_delbr(br_name); - } - } - - if (os_strcmp(ifname, vlan->ifname) == 0) { - if (vlan == first) { - hapd->conf->vlan = vlan->next; - } else { - prev->next = vlan->next; - } - os_free(vlan); - - break; - } - prev = vlan; - vlan = vlan->next; - } -} - - -static void -vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, - struct hostapd_data *hapd) -{ - struct ifinfomsg *ifi; - int attrlen, nlmsg_len, rta_len; - struct rtattr *attr; - char ifname[IFNAMSIZ + 1]; - - if (len < sizeof(*ifi)) - return; - - ifi = NLMSG_DATA(h); - - nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = h->nlmsg_len - nlmsg_len; - if (attrlen < 0) - return; - - attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); - - os_memset(ifname, 0, sizeof(ifname)); - rta_len = RTA_ALIGN(sizeof(struct rtattr)); - while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_IFNAME) { - int n = attr->rta_len - rta_len; - if (n < 0) - break; - - if ((size_t) n >= sizeof(ifname)) - n = sizeof(ifname) - 1; - os_memcpy(ifname, ((char *) attr) + rta_len, n); - - } - - attr = RTA_NEXT(attr, attrlen); - } - - if (!ifname[0]) - return; - if (del && if_nametoindex(ifname)) { - /* interface still exists, race condition -> - * iface has just been recreated */ - return; - } - - wpa_printf(MSG_DEBUG, - "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", - del ? "DEL" : "NEW", - ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags, - (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", - (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", - (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", - (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); - - if (del) - vlan_dellink(ifname, hapd); else - vlan_newlink(ifname, hapd); -} - - -static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) -{ - char buf[8192]; - int left; - struct sockaddr_nl from; - socklen_t fromlen; - struct nlmsghdr *h; - struct hostapd_data *hapd = eloop_ctx; - - fromlen = sizeof(from); - left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, - (struct sockaddr *) &from, &fromlen); - if (left < 0) { - if (errno != EINTR && errno != EAGAIN) - wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s", - __func__, strerror(errno)); - return; - } - - h = (struct nlmsghdr *) buf; - while (NLMSG_OK(h, left)) { - int len, plen; - - len = h->nlmsg_len; - plen = len - sizeof(*h); - if (len > left || plen < 0) { - wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink " - "message: len=%d left=%d plen=%d", - len, left, plen); - break; - } - - switch (h->nlmsg_type) { - case RTM_NEWLINK: - vlan_read_ifnames(h, plen, 0, hapd); - break; - case RTM_DELLINK: - vlan_read_ifnames(h, plen, 1, hapd); - break; - } + ret = 0; - h = NLMSG_NEXT(h, left); - } + if (ret) + return ret; - if (left > 0) { - wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of " - "netlink message", __func__, left); - } -} + ifconfig_up(vlan->ifname); /* else wpa group will fail fatal */ + if (hapd->wpa_auth) + ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id); -static struct full_dynamic_vlan * -full_dynamic_vlan_init(struct hostapd_data *hapd) -{ - struct sockaddr_nl local; - struct full_dynamic_vlan *priv; + if (ret == 0) + return ret; - priv = os_zalloc(sizeof(*priv)); - if (priv == NULL) - return NULL; + wpa_printf(MSG_ERROR, "WPA initialization for VLAN %d failed (%d)", + vlan->vlan_id, ret); + if (wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id)) + wpa_printf(MSG_ERROR, "WPA deinit of %s failed", vlan->ifname); -#ifndef CONFIG_VLAN_NETLINK - vlan_set_name_type(hapd->conf->ssid.vlan_naming == - DYNAMIC_VLAN_NAMING_WITH_DEVICE ? - VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD : - VLAN_NAME_TYPE_PLUS_VID_NO_PAD); -#endif /* CONFIG_VLAN_NETLINK */ + /* group state machine setup failed */ + if (hostapd_vlan_if_remove(hapd, vlan->ifname)) + wpa_printf(MSG_ERROR, "Removal of %s failed", vlan->ifname); - priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (priv->s < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW," - "NETLINK_ROUTE) failed: %s", - __func__, strerror(errno)); - os_free(priv); - return NULL; - } - - os_memset(&local, 0, sizeof(local)); - local.nl_family = AF_NETLINK; - local.nl_groups = RTMGRP_LINK; - if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) { - wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s", - __func__, strerror(errno)); - close(priv->s); - os_free(priv); - return NULL; - } - - if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL)) - { - close(priv->s); - os_free(priv); - return NULL; - } - - return priv; -} - - -static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) -{ - if (priv == NULL) - return; - eloop_unregister_read_sock(priv->s); - close(priv->s); - os_free(priv); + return ret; } -#endif /* CONFIG_FULL_DYNAMIC_VLAN */ -int vlan_setup_encryption_dyn(struct hostapd_data *hapd, const char *dyn_vlan) +int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan) { - int i; + int ret; - if (dyn_vlan == NULL) - return 0; + ret = wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id); + if (ret) + wpa_printf(MSG_ERROR, + "WPA deinitialization for VLAN %d failed (%d)", + vlan->vlan_id, ret); - /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own - * functions for setting up dynamic broadcast keys. */ - for (i = 0; i < 4; i++) { - if (hapd->conf->ssid.wep.key[i] && - hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i, - i == hapd->conf->ssid.wep.idx, NULL, 0, - hapd->conf->ssid.wep.key[i], - hapd->conf->ssid.wep.len[i])) - { - wpa_printf(MSG_ERROR, "VLAN: Could not set WEP " - "encryption for dynamic VLAN"); - return -1; - } - } - - return 0; + return hostapd_vlan_if_remove(hapd, vlan->ifname); } @@ -913,17 +83,14 @@ static int vlan_dynamic_add(struct hostapd_data *hapd, { while (vlan) { if (vlan->vlan_id != VLAN_ID_WILDCARD) { - if (hostapd_vlan_if_add(hapd, vlan->ifname)) { - if (errno != EEXIST) { - wpa_printf(MSG_ERROR, "VLAN: Could " - "not add VLAN %s: %s", - vlan->ifname, - strerror(errno)); - return -1; - } + if (vlan_if_add(hapd, vlan, 1)) { + wpa_printf(MSG_ERROR, + "VLAN: Could not add VLAN %s: %s", + vlan->ifname, strerror(errno)); + return -1; } #ifdef CONFIG_FULL_DYNAMIC_VLAN - ifconfig_up(vlan->ifname); + vlan_newlink(vlan->ifname, hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ } @@ -942,15 +109,17 @@ static void vlan_dynamic_remove(struct hostapd_data *hapd, while (vlan) { next = vlan->next; +#ifdef CONFIG_FULL_DYNAMIC_VLAN + /* vlan_dellink() takes care of cleanup and interface removal */ + if (vlan->vlan_id != VLAN_ID_WILDCARD) + vlan_dellink(vlan->ifname, hapd); +#else /* CONFIG_FULL_DYNAMIC_VLAN */ if (vlan->vlan_id != VLAN_ID_WILDCARD && - hostapd_vlan_if_remove(hapd, vlan->ifname)) { + vlan_if_remove(hapd, vlan)) { wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN " "iface: %s: %s", vlan->ifname, strerror(errno)); } -#ifdef CONFIG_FULL_DYNAMIC_VLAN - if (vlan->clean) - vlan_dellink(vlan->ifname, hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ vlan = next; @@ -964,7 +133,8 @@ int vlan_init(struct hostapd_data *hapd) hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ - if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED && + if ((hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED || + hapd->conf->ssid.per_sta_vif) && !hapd->conf->vlan) { /* dynamic vlans enabled but no (or empty) vlan_file given */ struct hostapd_vlan *vlan; @@ -1002,50 +172,45 @@ void vlan_deinit(struct hostapd_data *hapd) struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, - int vlan_id) + int vlan_id, + struct vlan_description *vlan_desc) { - struct hostapd_vlan *n = NULL; - char *ifname, *pos; + struct hostapd_vlan *n; + char ifname[IFNAMSIZ + 1], *pos; - if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID || - vlan->vlan_id != VLAN_ID_WILDCARD) + if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD) return NULL; wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)", __func__, vlan_id, vlan->ifname); - ifname = os_strdup(vlan->ifname); - if (ifname == NULL) - return NULL; + os_strlcpy(ifname, vlan->ifname, sizeof(ifname)); pos = os_strchr(ifname, '#'); if (pos == NULL) - goto free_ifname; + return NULL; *pos++ = '\0'; n = os_zalloc(sizeof(*n)); if (n == NULL) - goto free_ifname; + return NULL; n->vlan_id = vlan_id; + if (vlan_desc) + n->vlan_desc = *vlan_desc; n->dynamic_vlan = 1; os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, pos); - if (hostapd_vlan_if_add(hapd, n->ifname)) { - os_free(n); - n = NULL; - goto free_ifname; - } - n->next = hapd->conf->vlan; hapd->conf->vlan = n; -#ifdef CONFIG_FULL_DYNAMIC_VLAN - ifconfig_up(n->ifname); -#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + /* hapd->conf->vlan needs this new VLAN here for WPA setup */ + if (vlan_if_add(hapd, n, 0)) { + hapd->conf->vlan = n->next; + os_free(n); + n = NULL; + } -free_ifname: - os_free(ifname); return n; } @@ -1054,7 +219,7 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) { struct hostapd_vlan *vlan; - if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) + if (vlan_id <= 0) return 1; wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)", @@ -1073,7 +238,7 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) return 1; if (vlan->dynamic_vlan == 0) { - hostapd_vlan_if_remove(hapd, vlan->ifname); + vlan_if_remove(hapd, vlan); #ifdef CONFIG_FULL_DYNAMIC_VLAN vlan_dellink(vlan->ifname, hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ diff --git a/src/ap/vlan_init.h b/src/ap/vlan_init.h index fc39443e5d34b..d17c82c326ab6 100644 --- a/src/ap/vlan_init.h +++ b/src/ap/vlan_init.h @@ -15,10 +15,9 @@ int vlan_init(struct hostapd_data *hapd); void vlan_deinit(struct hostapd_data *hapd); struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, - int vlan_id); + int vlan_id, + struct vlan_description *vlan_desc); int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id); -int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - const char *dyn_vlan); #else /* CONFIG_NO_VLAN */ static inline int vlan_init(struct hostapd_data *hapd) { @@ -29,9 +28,9 @@ static inline void vlan_deinit(struct hostapd_data *hapd) { } -static inline struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, - struct hostapd_vlan *vlan, - int vlan_id) +static inline struct hostapd_vlan * +vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, + int vlan_id, struct vlan_description *vlan_desc) { return NULL; } @@ -40,12 +39,6 @@ static inline int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) { return -1; } - -static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - const char *dyn_vlan) -{ - return -1; -} #endif /* CONFIG_NO_VLAN */ #endif /* VLAN_INIT_H */ diff --git a/src/ap/vlan_ioctl.c b/src/ap/vlan_ioctl.c new file mode 100644 index 0000000000000..987b612e1d9f8 --- /dev/null +++ b/src/ap/vlan_ioctl.c @@ -0,0 +1,155 @@ +/* + * hostapd / VLAN ioctl API + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include <sys/ioctl.h> + +#include "utils/common.h" +#include "common/linux_vlan.h" +#include "vlan_util.h" + + +int vlan_rem(const char *if_name) +{ + int fd; + struct vlan_ioctl_args if_request; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name); + if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); + return -1; + } + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); + if_request.cmd = DEL_VLAN_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: " + "%s", __func__, if_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add a vlan interface with VLAN ID 'vid' and tagged interface + 'if_name'. + + returns -1 on error + returns 1 if the interface already exists + returns 0 otherwise +*/ +int vlan_add(const char *if_name, int vid, const char *vlan_if_name) +{ + int fd; + struct vlan_ioctl_args if_request; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)", + if_name, vid); + ifconfig_up(if_name); + + if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); + return -1; + } + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + /* Determine if a suitable vlan device already exists. */ + + os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d", + vid); + + if_request.cmd = GET_VLAN_VID_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && + if_request.u.VID == vid) { + if_request.cmd = GET_VLAN_REALDEV_NAME_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && + os_strncmp(if_request.u.device2, if_name, + sizeof(if_request.u.device2)) == 0) { + close(fd); + wpa_printf(MSG_DEBUG, + "VLAN: vlan_add: if_name %s exists already", + if_request.device1); + return 1; + } + } + + /* A suitable vlan device does not already exist, add one. */ + + os_memset(&if_request, 0, sizeof(if_request)); + os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); + if_request.u.VID = vid; + if_request.cmd = ADD_VLAN_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + wpa_printf(MSG_ERROR, + "VLAN: %s: ADD_VLAN_CMD failed for %s: %s", + __func__, if_request.device1, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +int vlan_set_name_type(unsigned int name_type) +{ + int fd; + struct vlan_ioctl_args if_request; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)", + name_type); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, + "VLAN: %s: socket(AF_INET,SOCK_STREAM) failed: %s", + __func__, strerror(errno)); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + if_request.u.name_type = name_type; + if_request.cmd = SET_VLAN_NAME_TYPE_CMD; + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + wpa_printf(MSG_ERROR, + "VLAN: %s: SET_VLAN_NAME_TYPE_CMD name_type=%u failed: %s", + __func__, name_type, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} diff --git a/src/ap/vlan_util.c b/src/ap/vlan_util.c index d4e0efb9b0242..56d1d3d123e87 100644 --- a/src/ap/vlan_util.c +++ b/src/ap/vlan_util.c @@ -7,18 +7,10 @@ */ #include "utils/includes.h" -#include <sys/ioctl.h> -#include <linux/sockios.h> -#include <linux/if_vlan.h> -#include <netlink/genl/genl.h> -#include <netlink/genl/family.h> -#include <netlink/genl/ctrl.h> #include <netlink/route/link.h> #include <netlink/route/link/vlan.h> #include "utils/common.h" -#include "utils/eloop.h" -#include "hostapd.h" #include "vlan_util.h" /* @@ -33,7 +25,6 @@ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) { int err, ret = -1; struct nl_sock *handle = NULL; - struct nl_cache *cache = NULL; struct rtnl_link *rlink = NULL; int if_idx = 0; @@ -65,22 +56,19 @@ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) goto vlan_add_error; } - err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache); + err = rtnl_link_get_kernel(handle, 0, if_name, &rlink); if (err < 0) { - cache = NULL; - wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s", - nl_geterror(err)); - goto vlan_add_error; - } - - if (!(if_idx = rtnl_link_name2i(cache, if_name))) { /* link does not exist */ wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist", if_name); goto vlan_add_error; } + if_idx = rtnl_link_get_ifindex(rlink); + rtnl_link_put(rlink); + rlink = NULL; - if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) { + err = rtnl_link_get_kernel(handle, 0, vlan_if_name, &rlink); + if (err >= 0) { /* link does exist */ rtnl_link_put(rlink); rlink = NULL; @@ -127,8 +115,6 @@ int vlan_add(const char *if_name, int vid, const char *vlan_if_name) vlan_add_error: if (rlink) rtnl_link_put(rlink); - if (cache) - nl_cache_free(cache); if (handle) nl_socket_free(handle); return ret; @@ -139,7 +125,6 @@ int vlan_rem(const char *if_name) { int err, ret = -1; struct nl_sock *handle = NULL; - struct nl_cache *cache = NULL; struct rtnl_link *rlink = NULL; wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name); @@ -157,15 +142,8 @@ int vlan_rem(const char *if_name) goto vlan_rem_error; } - err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache); + err = rtnl_link_get_kernel(handle, 0, if_name, &rlink); if (err < 0) { - cache = NULL; - wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s", - nl_geterror(err)); - goto vlan_rem_error; - } - - if (!(rlink = rtnl_link_get_by_name(cache, if_name))) { /* link does not exist */ wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists", if_name); @@ -184,9 +162,13 @@ int vlan_rem(const char *if_name) vlan_rem_error: if (rlink) rtnl_link_put(rlink); - if (cache) - nl_cache_free(cache); if (handle) nl_socket_free(handle); return ret; } + + +int vlan_set_name_type(unsigned int name_type) +{ + return 0; +} diff --git a/src/ap/vlan_util.h b/src/ap/vlan_util.h index bef5a16f6c909..244685975c0be 100644 --- a/src/ap/vlan_util.h +++ b/src/ap/vlan_util.h @@ -1,5 +1,5 @@ /* - * hostapd / VLAN netlink api + * hostapd / VLAN netlink/ioctl api * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de> * * This software may be distributed under the terms of the BSD license. @@ -9,7 +9,23 @@ #ifndef VLAN_UTIL_H #define VLAN_UTIL_H +struct hostapd_data; +struct hostapd_vlan; +struct full_dynamic_vlan; + int vlan_add(const char *if_name, int vid, const char *vlan_if_name); int vlan_rem(const char *if_name); +int vlan_set_name_type(unsigned int name_type); + +int ifconfig_helper(const char *if_name, int up); +int ifconfig_up(const char *if_name); +int iface_exists(const char *ifname); +int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan); + +struct full_dynamic_vlan * +full_dynamic_vlan_init(struct hostapd_data *hapd); +void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv); +void vlan_newlink(const char *ifname, struct hostapd_data *hapd); +void vlan_dellink(const char *ifname, struct hostapd_data *hapd); #endif /* VLAN_UTIL_H */ diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c index 4c8bc10083c4e..41d50cebfbe0a 100644 --- a/src/ap/wnm_ap.c +++ b/src/ap/wnm_ap.c @@ -17,6 +17,7 @@ #include "ap/ap_config.h" #include "ap/ap_drv_ops.h" #include "ap/wpa_auth.h" +#include "mbo_ap.h" #include "wnm_ap.h" #define MAX_TFS_IE_LEN 1024 @@ -94,6 +95,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, if (mgmt == NULL) { wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " "WNM-Sleep Response action frame"); + os_free(wnmtfs_ie); return -1; } os_memcpy(mgmt->da, addr, ETH_ALEN); @@ -376,6 +378,29 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, } +static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *buf, + size_t len) +{ + u8 dialog_token, type; + + if (len < 2) + return; + dialog_token = *buf++; + type = *buf++; + len -= 2; + + wpa_printf(MSG_DEBUG, + "WNM: Received WNM Notification Request frame from " + MACSTR " (dialog_token=%u type=%u)", + MAC2STR(addr), dialog_token, type); + wpa_hexdump(MSG_MSGDUMP, "WNM: Notification Request subelements", + buf, len); + if (type == WLAN_EID_VENDOR_SPECIFIC) + mbo_ap_wnm_notification_req(hapd, addr, buf, len); +} + + int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -402,6 +427,10 @@ int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, case WNM_SLEEP_MODE_REQ: ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen); return 0; + case WNM_NOTIFICATION_REQ: + ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload, + plen); + return 0; } wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, @@ -527,7 +556,8 @@ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, u8 req_mode, int disassoc_timer, u8 valid_int, const u8 *bss_term_dur, const char *url, - const u8 *nei_rep, size_t nei_rep_len) + const u8 *nei_rep, size_t nei_rep_len, + const u8 *mbo_attrs, size_t mbo_len) { u8 *buf, *pos; struct ieee80211_mgmt *mgmt; @@ -536,7 +566,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x", MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int); - buf = os_zalloc(1000 + nei_rep_len); + buf = os_zalloc(1000 + nei_rep_len + mbo_len); if (buf == NULL) return -1; mgmt = (struct ieee80211_mgmt *) buf; @@ -579,6 +609,11 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, pos += nei_rep_len; } + if (mbo_len > 0) { + pos += mbo_add_ie(pos, buf + sizeof(buf) - pos, mbo_attrs, + mbo_len); + } + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { wpa_printf(MSG_DEBUG, "Failed to send BSS Transition Management Request frame"); diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h index 7789307209c99..a44eadb85e555 100644 --- a/src/ap/wnm_ap.h +++ b/src/ap/wnm_ap.h @@ -21,6 +21,7 @@ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, u8 req_mode, int disassoc_timer, u8 valid_int, const u8 *bss_term_dur, const char *url, - const u8 *nei_rep, size_t nei_rep_len); + const u8 *nei_rep, size_t nei_rep_len, + const u8 *mbo_attrs, size_t mbo_len); #endif /* WNM_AP_H */ diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 2760a3f3a00e5..358708648977e 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -44,7 +44,8 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, - const u8 *pmk, struct wpa_ptk *ptk); + const u8 *pmk, unsigned int pmk_len, + struct wpa_ptk *ptk); static void wpa_group_free(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_group_get(struct wpa_authenticator *wpa_auth, @@ -827,6 +828,7 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, struct wpa_ptk PTK; int ok = 0; const u8 *pmk = NULL; + unsigned int pmk_len; for (;;) { if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { @@ -834,10 +836,13 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, sm->p2p_dev_addr, pmk); if (pmk == NULL) break; - } else + pmk_len = PMK_LEN; + } else { pmk = sm->PMK; + pmk_len = sm->pmk_len; + } - wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK); + wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK); if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len) == 0) { @@ -1904,11 +1909,27 @@ SM_STATE(WPA_PTK, INITPMK) #endif /* CONFIG_IEEE80211R */ if (sm->pmksa) { wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); - os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN); + os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len); + sm->pmk_len = sm->pmksa->pmk_len; } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) { + unsigned int pmk_len; + + if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + pmk_len = PMK_LEN_SUITE_B_192; + else + pmk_len = PMK_LEN; wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " - "(len=%lu)", (unsigned long) len); - os_memcpy(sm->PMK, msk, PMK_LEN); + "(MSK len=%lu PMK len=%u)", (unsigned long) len, + pmk_len); + if (len < pmk_len) { + wpa_printf(MSG_DEBUG, + "WPA: MSK not long enough (%u) to create PMK (%u)", + (unsigned int) len, (unsigned int) pmk_len); + sm->Disconnect = TRUE; + return; + } + os_memcpy(sm->PMK, msk, pmk_len); + sm->pmk_len = pmk_len; #ifdef CONFIG_IEEE80211R if (len >= 2 * PMK_LEN) { os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); @@ -1943,6 +1964,7 @@ SM_STATE(WPA_PTK, INITPSK) psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL); if (psk) { os_memcpy(sm->PMK, psk, PMK_LEN); + sm->pmk_len = PMK_LEN; #ifdef CONFIG_IEEE80211R os_memcpy(sm->xxkey, psk, PMK_LEN); sm->xxkey_len = PMK_LEN; @@ -1994,7 +2016,7 @@ SM_STATE(WPA_PTK, PTKSTART) * Calculate PMKID since no PMKSA cache entry was * available with pre-calculated PMKID. */ - rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr, + rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr, sm->addr, &pmkid[2 + RSN_SELECTOR_LEN], wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); } @@ -2006,14 +2028,15 @@ SM_STATE(WPA_PTK, PTKSTART) static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, - const u8 *pmk, struct wpa_ptk *ptk) + const u8 *pmk, unsigned int pmk_len, + struct wpa_ptk *ptk) { #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return wpa_auth_derive_ptk_ft(sm, pmk, ptk); #endif /* CONFIG_IEEE80211R */ - return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", + 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); } @@ -2024,6 +2047,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) struct wpa_ptk PTK; int ok = 0, psk_found = 0; const u8 *pmk = NULL; + unsigned int pmk_len; SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; @@ -2039,10 +2063,13 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) if (pmk == NULL) break; psk_found = 1; - } else + pmk_len = PMK_LEN; + } else { pmk = sm->PMK; + pmk_len = sm->pmk_len; + } - wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK); + wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK); if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, sm->last_rx_eapol_key, @@ -2092,6 +2119,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) * state machine data based on whatever PSK was selected here. */ os_memcpy(sm->PMK, pmk, PMK_LEN); + sm->pmk_len = PMK_LEN; } sm->MICVerified = TRUE; @@ -2270,14 +2298,19 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) pos += wpa_ie_len; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { - int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name); + int res; + size_t elen; + + elen = pos - kde; + res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name); if (res < 0) { wpa_printf(MSG_ERROR, "FT: Failed to insert " "PMKR1Name into RSN IE in EAPOL-Key data"); os_free(kde); return; } - pos += res; + pos -= wpa_ie_len; + pos += elen; } #endif /* CONFIG_IEEE80211R */ if (gtk) { @@ -2295,10 +2328,18 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) struct wpa_auth_config *conf; conf = &sm->wpa_auth->conf; - res = wpa_write_ftie(conf, conf->r0_key_holder, - conf->r0_key_holder_len, - NULL, NULL, pos, kde + kde_len - pos, - NULL, 0); + if (sm->assoc_resp_ftie && + kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) { + os_memcpy(pos, sm->assoc_resp_ftie, + 2 + sm->assoc_resp_ftie[1]); + res = 2 + sm->assoc_resp_ftie[1]; + } else { + res = wpa_write_ftie(conf, conf->r0_key_holder, + conf->r0_key_holder_len, + NULL, NULL, pos, + kde + kde_len - pos, + NULL, 0); + } if (res < 0) { wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE " "into EAPOL-Key Key Data"); @@ -3243,13 +3284,21 @@ const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len) int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + unsigned int pmk_len, int session_timeout, struct eapol_state_machine *eapol) { if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 || sm->wpa_auth->conf.disable_pmksa_caching) return -1; - if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, + if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + if (pmk_len > PMK_LEN_SUITE_B_192) + pmk_len = PMK_LEN_SUITE_B_192; + } else if (pmk_len > PMK_LEN) { + pmk_len = PMK_LEN; + } + + if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len, NULL, sm->PTK.kck, sm->PTK.kck_len, sm->wpa_auth->addr, sm->addr, session_timeout, eapol, sm->wpa_key_mgmt)) @@ -3267,7 +3316,7 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, if (wpa_auth == NULL) return -1; - if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, + if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL, NULL, 0, wpa_auth->addr, sta_addr, session_timeout, eapol, @@ -3279,12 +3328,12 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, - const u8 *pmk) + const u8 *pmk, const u8 *pmkid) { if (wpa_auth->conf.disable_pmksa_caching) return -1; - if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, + if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid, NULL, 0, wpa_auth->addr, addr, 0, NULL, WPA_KEY_MGMT_SAE)) @@ -3310,6 +3359,46 @@ void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, } +int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, + size_t len) +{ + if (!wpa_auth || !wpa_auth->pmksa) + return 0; + return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len); +} + + +void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth) +{ + if (wpa_auth && wpa_auth->pmksa) + pmksa_cache_auth_flush(wpa_auth->pmksa); +} + + +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) +{ + if (!wpa_auth || !wpa_auth->pmksa) + return NULL; + return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL); +} + + +void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa, + struct wpa_state_machine *sm, + struct wpa_authenticator *wpa_auth, + u8 *pmkid, u8 *pmk) +{ + if (!sm) + return; + + sm->pmksa = pmksa; + os_memcpy(pmk, pmksa->pmk, PMK_LEN); + os_memcpy(pmkid, pmksa->pmkid, PMKID_LEN); + os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmksa->pmkid, PMKID_LEN); +} + + /* * Remove and free the group from wpa_authenticator. This is triggered by a * callback to make sure nobody is currently iterating the group list while it @@ -3388,6 +3477,98 @@ wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) } +/* + * Enforce that the group state machine for the VLAN is running, increase + * reference counter as interface is up. References might have been increased + * even if a negative value is returned. + * Returns: -1 on error (group missing, group already failed); otherwise, 0 + */ +int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id) +{ + struct wpa_group *group; + + if (wpa_auth == NULL) + return 0; + + group = wpa_auth->group; + while (group) { + if (group->vlan_id == vlan_id) + break; + group = group->next; + } + + if (group == NULL) { + group = wpa_auth_add_group(wpa_auth, vlan_id); + if (group == NULL) + return -1; + } + + wpa_printf(MSG_DEBUG, + "WPA: Ensure group state machine running for VLAN ID %d", + vlan_id); + + wpa_group_get(wpa_auth, group); + group->num_setup_iface++; + + if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) + return -1; + + return 0; +} + + +/* + * Decrease reference counter, expected to be zero afterwards. + * returns: -1 on error (group not found, group in fail state) + * -2 if wpa_group is still referenced + * 0 else + */ +int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id) +{ + struct wpa_group *group; + int ret = 0; + + if (wpa_auth == NULL) + return 0; + + group = wpa_auth->group; + while (group) { + if (group->vlan_id == vlan_id) + break; + group = group->next; + } + + if (group == NULL) + return -1; + + wpa_printf(MSG_DEBUG, + "WPA: Try stopping group state machine for VLAN ID %d", + vlan_id); + + if (group->num_setup_iface <= 0) { + wpa_printf(MSG_ERROR, + "WPA: wpa_auth_release_group called more often than wpa_auth_ensure_group for VLAN ID %d, skipping.", + vlan_id); + return -1; + } + group->num_setup_iface--; + + if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) + ret = -1; + + if (group->references > 1) { + wpa_printf(MSG_DEBUG, + "WPA: Cannot stop group state machine for VLAN ID %d as references are still hold", + vlan_id); + ret = -2; + } + + wpa_group_put(wpa_auth, group); + + return ret; +} + + int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) { struct wpa_group *group; diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index fd04f169433af..0de8d976e3f92 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -42,10 +42,11 @@ struct ft_rrb_frame { #define FT_PACKET_R0KH_R1KH_RESP 201 #define FT_PACKET_R0KH_R1KH_PUSH 202 -#define FT_R0KH_R1KH_PULL_DATA_LEN 44 -#define FT_R0KH_R1KH_RESP_DATA_LEN 76 -#define FT_R0KH_R1KH_PUSH_DATA_LEN 88 #define FT_R0KH_R1KH_PULL_NONCE_LEN 16 +#define FT_R0KH_R1KH_PULL_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \ + WPA_PMK_NAME_LEN + FT_R1KH_ID_LEN + \ + ETH_ALEN) +#define FT_R0KH_R1KH_PULL_PAD_LEN ((8 - FT_R0KH_R1KH_PULL_DATA_LEN % 8) % 8) struct ft_r0kh_r1kh_pull_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ @@ -57,14 +58,18 @@ struct ft_r0kh_r1kh_pull_frame { u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 r1kh_id[FT_R1KH_ID_LEN]; u8 s1kh_id[ETH_ALEN]; - u8 pad[4]; /* 8-octet boundary for AES key wrap */ + u8 pad[FT_R0KH_R1KH_PULL_PAD_LEN]; /* 8-octet boundary for AES block */ u8 key_wrap_extra[8]; } STRUCT_PACKED; +#define FT_R0KH_R1KH_RESP_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \ + FT_R1KH_ID_LEN + ETH_ALEN + PMK_LEN + \ + WPA_PMK_NAME_LEN + 2) +#define FT_R0KH_R1KH_RESP_PAD_LEN ((8 - FT_R0KH_R1KH_RESP_DATA_LEN % 8) % 8) struct ft_r0kh_r1kh_resp_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */ - le16 data_length; /* little endian length of data (76) */ + le16 data_length; /* little endian length of data (78) */ u8 ap_address[ETH_ALEN]; u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */ @@ -73,14 +78,18 @@ struct ft_r0kh_r1kh_resp_frame { u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; le16 pairwise; - u8 pad[2]; /* 8-octet boundary for AES key wrap */ + u8 pad[FT_R0KH_R1KH_RESP_PAD_LEN]; /* 8-octet boundary for AES block */ u8 key_wrap_extra[8]; } STRUCT_PACKED; +#define FT_R0KH_R1KH_PUSH_DATA_LEN (4 + FT_R1KH_ID_LEN + ETH_ALEN + \ + WPA_PMK_NAME_LEN + PMK_LEN + \ + WPA_PMK_NAME_LEN + 2) +#define FT_R0KH_R1KH_PUSH_PAD_LEN ((8 - FT_R0KH_R1KH_PUSH_DATA_LEN % 8) % 8) struct ft_r0kh_r1kh_push_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */ - le16 data_length; /* little endian length of data (88) */ + le16 data_length; /* little endian length of data (82) */ u8 ap_address[ETH_ALEN]; /* Encrypted with AES key-wrap */ @@ -92,7 +101,7 @@ struct ft_r0kh_r1kh_push_frame { u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; le16 pairwise; - u8 pad[6]; /* 8-octet boundary for AES key wrap */ + u8 pad[FT_R0KH_R1KH_PUSH_PAD_LEN]; /* 8-octet boundary for AES block */ u8 key_wrap_extra[8]; } STRUCT_PACKED; @@ -279,15 +288,25 @@ void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm); const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len); int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + unsigned int pmk_len, int session_timeout, struct eapol_state_machine *eapol); int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, const u8 *pmk, size_t len, const u8 *sta_addr, int session_timeout, struct eapol_state_machine *eapol); int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, - const u8 *pmk); + const u8 *pmk, const u8 *pmkid); void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); +int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, + size_t len); +void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth); +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); +void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa, + struct wpa_state_machine *sm, + struct wpa_authenticator *wpa_auth, + u8 *pmkid, u8 *pmk); int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack); @@ -325,4 +344,7 @@ int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth, struct radius_das_attrs *attr); void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth); +int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id); +int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id); + #endif /* WPA_AUTH_H */ diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index eeaffbf635166..42242a54a2cbf 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -720,11 +720,6 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, ftie_len = res; pos += res; - os_free(sm->assoc_resp_ftie); - sm->assoc_resp_ftie = os_malloc(ftie_len); - if (sm->assoc_resp_ftie) - os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); - _ftie = (struct rsn_ftie *) (ftie + 2); if (auth_alg == WLAN_AUTH_FT) _ftie->mic_control[1] = 3; /* Information element count */ @@ -750,6 +745,11 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, _ftie->mic) < 0) wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + os_free(sm->assoc_resp_ftie); + sm->assoc_resp_ftie = os_malloc(ftie_len); + if (sm->assoc_resp_ftie) + os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); + return pos; } diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index f98cc50599e37..21424147e443e 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -12,6 +12,7 @@ #include "common/ieee802_11_defs.h" #include "common/sae.h" #include "common/wpa_ctrl.h" +#include "crypto/sha1.h" #include "eapol_auth/eapol_auth_sm.h" #include "eapol_auth/eapol_auth_sm_i.h" #include "eap_server/eap.h" @@ -246,6 +247,13 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, struct hostapd_sta_wpa_psk_short *pos; psk = sta->psk->psk; for (pos = sta->psk; pos; pos = pos->next) { + if (pos->is_passphrase) { + pbkdf2_sha1(pos->passphrase, + hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len, 4096, + pos->psk, PMK_LEN); + pos->is_passphrase = 0; + } if (pos->psk == prev_psk) { psk = pos->next ? pos->next->psk : NULL; break; @@ -413,6 +421,8 @@ static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx) hapd = iface->bss[j]; if (hapd == idata->src_hapd) continue; + if (!hapd->wpa_auth) + continue; if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) { wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to " "locally managed BSS " MACSTR "@%s -> " @@ -563,6 +573,9 @@ static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, ethhdr = (struct l2_ethhdr *) buf; wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> " MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest)); + if (!is_multicast_ether_addr(ethhdr->h_dest) && + os_memcmp(hapd->own_addr, ethhdr->h_dest, ETH_ALEN) != 0) + return; wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr), len - sizeof(*ethhdr)); } @@ -637,7 +650,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) } #ifdef CONFIG_IEEE80211R - if (!hostapd_drv_none(hapd) && hapd->conf->ft_over_ds && + if (!hostapd_drv_none(hapd) && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) { hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ? hapd->conf->bridge : @@ -674,13 +687,14 @@ void hostapd_deinit_wpa(struct hostapd_data *hapd) wpa_deinit(hapd->wpa_auth); hapd->wpa_auth = NULL; - if (hostapd_set_privacy(hapd, 0)) { + if (hapd->drv_priv && hostapd_set_privacy(hapd, 0)) { wpa_printf(MSG_DEBUG, "Could not disable " "PrivacyInvoked for interface %s", hapd->conf->iface); } - if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { + if (hapd->drv_priv && + hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { wpa_printf(MSG_DEBUG, "Could not remove generic " "information element from interface %s", hapd->conf->iface); diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index 57b098f2ed72a..72b7eb37a3a7a 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -60,7 +60,8 @@ struct wpa_state_machine { u8 SNonce[WPA_NONCE_LEN]; u8 alt_SNonce[WPA_NONCE_LEN]; u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN]; - u8 PMK[PMK_LEN]; + u8 PMK[PMK_LEN_MAX]; + unsigned int pmk_len; struct wpa_ptk PTK; Boolean PTK_valid; Boolean pairwise_set; @@ -171,6 +172,7 @@ struct wpa_group { #endif /* CONFIG_IEEE80211W */ /* Number of references except those in struct wpa_group->next */ unsigned int references; + unsigned int num_setup_iface; }; diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index eafb828b8d60e..f79783b91929b 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -251,7 +251,7 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, pos += 2; if (pmkid) { - if (pos + 2 + PMKID_LEN > buf + len) + if (2 + PMKID_LEN > buf + len - pos) return -1; /* PMKID Count */ WPA_PUT_LE16(pos, 1); @@ -263,7 +263,7 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, #ifdef CONFIG_IEEE80211W if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION && conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) { - if (pos + 2 + 4 > buf + len) + if (2 + 4 > buf + len - pos) return -1; if (pmkid == NULL) { /* PMKID Count */ @@ -712,11 +712,14 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } } if (sm->pmksa && pmkid) { + struct vlan_description *vlan; + + vlan = sm->pmksa->vlan_desc; wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, - "PMKID found from PMKSA cache " - "eap_type=%d vlan_id=%d", + "PMKID found from PMKSA cache eap_type=%d vlan=%d%s", sm->pmksa->eap_type_authsrv, - sm->pmksa->vlan_id); + vlan ? vlan->untagged : 0, + (vlan && vlan->tagged[0]) ? "+" : ""); os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN); } @@ -791,7 +794,7 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, return 0; } - if (pos + 1 + RSN_SELECTOR_LEN < end && + if (1 + RSN_SELECTOR_LEN < end - pos && pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; @@ -887,13 +890,13 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) int ret = 0; os_memset(ie, 0, sizeof(*ie)); - for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) { if (pos[0] == 0xdd && ((pos == buf + len - 1) || pos[1] == 0)) { /* Ignore padding */ break; } - if (pos + 2 + pos[1] > end) { + if (2 + pos[1] > end - pos) { wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " "underflow (ie=%d len=%d pos=%d)", pos[0], pos[1], (int) (pos - buf)); diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index cde31e60e03b7..95b40da0f6bb4 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -1,6 +1,6 @@ /* * hostapd / WPS integration - * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2016, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -269,12 +269,6 @@ static void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr, } -static int str_starts(const char *str, const char *start) -{ - return os_strncmp(str, start, os_strlen(start)) == 0; -} - - static void wps_reload_config(void *eloop_data, void *user_ctx) { struct hostapd_iface *iface = eloop_data; @@ -445,6 +439,8 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len); hapd->wps->ssid_len = cred->ssid_len; hapd->wps->encr_types = cred->encr_type; + hapd->wps->encr_types_rsn = cred->encr_type; + hapd->wps->encr_types_wpa = cred->encr_type; hapd->wps->auth_types = cred->auth_type; hapd->wps->ap_encr_type = cred->encr_type; hapd->wps->ap_auth_type = cred->auth_type; @@ -872,7 +868,8 @@ static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only) hapd->wps_probe_resp_ie = NULL; if (deinit_only) { - hostapd_reset_ap_wps_ie(hapd); + if (hapd->drv_priv) + hostapd_reset_ap_wps_ie(hapd); return; } @@ -1067,10 +1064,14 @@ int hostapd_init_wps(struct hostapd_data *hapd, if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) wps->auth_types |= WPS_AUTH_WPA2; - if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) + if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) { wps->encr_types |= WPS_ENCR_AES; - if (conf->rsn_pairwise & WPA_CIPHER_TKIP) + wps->encr_types_rsn |= WPS_ENCR_AES; + } + if (conf->rsn_pairwise & WPA_CIPHER_TKIP) { wps->encr_types |= WPS_ENCR_TKIP; + wps->encr_types_rsn |= WPS_ENCR_TKIP; + } } if (conf->wpa & WPA_PROTO_WPA) { @@ -1079,10 +1080,14 @@ int hostapd_init_wps(struct hostapd_data *hapd, if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) wps->auth_types |= WPS_AUTH_WPA; - if (conf->wpa_pairwise & WPA_CIPHER_CCMP) + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { wps->encr_types |= WPS_ENCR_AES; - if (conf->wpa_pairwise & WPA_CIPHER_TKIP) + wps->encr_types_wpa |= WPS_ENCR_AES; + } + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { wps->encr_types |= WPS_ENCR_TKIP; + wps->encr_types_wpa |= WPS_ENCR_TKIP; + } } if (conf->ssid.security_policy == SECURITY_PLAINTEXT) { @@ -1122,6 +1127,8 @@ int hostapd_init_wps(struct hostapd_data *hapd, /* Override parameters to enable security by default */ wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP; + wps->encr_types_rsn = WPS_ENCR_AES | WPS_ENCR_TKIP; + wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP; } wps->ap_settings = conf->ap_settings; @@ -1614,7 +1621,8 @@ const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout) unsigned int pin; struct wps_ap_pin_data data; - pin = wps_generate_pin(); + if (wps_generate_pin(&pin) < 0) + return NULL; os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin); data.timeout = timeout; hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); diff --git a/src/common/cli.c b/src/common/cli.c new file mode 100644 index 0000000000000..b583d1cd575d9 --- /dev/null +++ b/src/common/cli.c @@ -0,0 +1,267 @@ +/* + * Common hostapd/wpa_supplicant command line interface functions + * Copyright (c) 2004-2016, 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 "utils/common.h" +#include "common/cli.h" + + +const char *const cli_license = +"This software may be distributed under the terms of the BSD license.\n" +"See README for more details.\n"; + +const char *const cli_full_license = +"This software may be distributed under the terms of the BSD license.\n" +"\n" +"Redistribution and use in source and binary forms, with or without\n" +"modification, are permitted provided that the following conditions are\n" +"met:\n" +"\n" +"1. Redistributions of source code must retain the above copyright\n" +" notice, this list of conditions and the following disclaimer.\n" +"\n" +"2. Redistributions in binary form must reproduce the above copyright\n" +" notice, this list of conditions and the following disclaimer in the\n" +" documentation and/or other materials provided with the distribution.\n" +"\n" +"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" +" names of its contributors may be used to endorse or promote products\n" +" derived from this software without specific prior written permission.\n" +"\n" +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" +"\n"; + + +void cli_txt_list_free(struct cli_txt_entry *e) +{ + dl_list_del(&e->list); + os_free(e->txt); + os_free(e); +} + + +void cli_txt_list_flush(struct dl_list *list) +{ + struct cli_txt_entry *e; + + while ((e = dl_list_first(list, struct cli_txt_entry, list))) + cli_txt_list_free(e); +} + + +struct cli_txt_entry * cli_txt_list_get(struct dl_list *txt_list, + const char *txt) +{ + struct cli_txt_entry *e; + + dl_list_for_each(e, txt_list, struct cli_txt_entry, list) { + if (os_strcmp(e->txt, txt) == 0) + return e; + } + return NULL; +} + + +void cli_txt_list_del(struct dl_list *txt_list, const char *txt) +{ + struct cli_txt_entry *e; + + e = cli_txt_list_get(txt_list, txt); + if (e) + cli_txt_list_free(e); +} + + +void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt) +{ + u8 addr[ETH_ALEN]; + char buf[18]; + + if (hwaddr_aton(txt, addr) < 0) + return; + os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + cli_txt_list_del(txt_list, buf); +} + + +void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt, + int separator) +{ + const char *end; + char *buf; + + end = os_strchr(txt, separator); + if (end == NULL) + end = txt + os_strlen(txt); + buf = dup_binstr(txt, end - txt); + if (buf == NULL) + return; + cli_txt_list_del(txt_list, buf); + os_free(buf); +} + + +int cli_txt_list_add(struct dl_list *txt_list, const char *txt) +{ + struct cli_txt_entry *e; + + e = cli_txt_list_get(txt_list, txt); + if (e) + return 0; + e = os_zalloc(sizeof(*e)); + if (e == NULL) + return -1; + e->txt = os_strdup(txt); + if (e->txt == NULL) { + os_free(e); + return -1; + } + dl_list_add(txt_list, &e->list); + return 0; +} + + +int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt) +{ + u8 addr[ETH_ALEN]; + char buf[18]; + + if (hwaddr_aton(txt, addr) < 0) + return -1; + os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + return cli_txt_list_add(txt_list, buf); +} + + +int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt, + int separator) +{ + const char *end; + char *buf; + int ret; + + end = os_strchr(txt, separator); + if (end == NULL) + end = txt + os_strlen(txt); + buf = dup_binstr(txt, end - txt); + if (buf == NULL) + return -1; + ret = cli_txt_list_add(txt_list, buf); + os_free(buf); + return ret; +} + + +char ** cli_txt_list_array(struct dl_list *txt_list) +{ + unsigned int i, count = dl_list_len(txt_list); + char **res; + struct cli_txt_entry *e; + + res = os_calloc(count + 1, sizeof(char *)); + if (res == NULL) + return NULL; + + i = 0; + dl_list_for_each(e, txt_list, struct cli_txt_entry, list) { + res[i] = os_strdup(e->txt); + if (res[i] == NULL) + break; + i++; + } + + return res; +} + + +int get_cmd_arg_num(const char *str, int pos) +{ + int arg = 0, i; + + for (i = 0; i <= pos; i++) { + if (str[i] != ' ') { + arg++; + while (i <= pos && str[i] != ' ') + i++; + } + } + + if (arg > 0) + arg--; + return arg; +} + + +int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, char *argv[]) +{ + int i, res; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + res = os_snprintf(pos, end - pos, "%s", cmd); + if (os_snprintf_error(end - pos, res)) + goto fail; + pos += res; + + for (i = 0; i < argc; i++) { + res = os_snprintf(pos, end - pos, " %s", argv[i]); + if (os_snprintf_error(end - pos, res)) + goto fail; + pos += res; + } + + buf[buflen - 1] = '\0'; + return 0; + +fail: + printf("Too long command\n"); + return -1; +} + + +int tokenize_cmd(char *cmd, char *argv[]) +{ + char *pos; + int argc = 0; + + pos = cmd; + for (;;) { + while (*pos == ' ') + pos++; + if (*pos == '\0') + break; + argv[argc] = pos; + argc++; + if (argc == max_args) + break; + if (*pos == '"') { + char *pos2 = os_strrchr(pos, '"'); + if (pos2) + pos = pos2 + 1; + } + while (*pos != '\0' && *pos != ' ') + pos++; + if (*pos == ' ') + *pos++ = '\0'; + } + + return argc; +} diff --git a/src/common/cli.h b/src/common/cli.h new file mode 100644 index 0000000000000..41ef329696ea9 --- /dev/null +++ b/src/common/cli.h @@ -0,0 +1,47 @@ +/* + * Common hostapd/wpa_supplicant command line interface functionality + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef CLI_H +#define CLI_H + +#include "utils/list.h" + +extern const char *const cli_license; +extern const char *const cli_full_license; + +struct cli_txt_entry { + struct dl_list list; + char *txt; +}; + +void cli_txt_list_free(struct cli_txt_entry *e); +void cli_txt_list_flush(struct dl_list *list); + +struct cli_txt_entry * +cli_txt_list_get(struct dl_list *txt_list, const char *txt); + +void cli_txt_list_del(struct dl_list *txt_list, const char *txt); +void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt); +void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt, + int separator); + +int cli_txt_list_add(struct dl_list *txt_list, const char *txt); +int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt); +int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt, + int separator); + +char ** cli_txt_list_array(struct dl_list *txt_list); + +int get_cmd_arg_num(const char *str, int pos); +int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, + char *argv[]); + +#define max_args 10 +int tokenize_cmd(char *cmd, char *argv[]); + +#endif /* CLI_H */ diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c index d69448bd3800c..e0769c08e764a 100644 --- a/src/common/common_module_tests.c +++ b/src/common/common_module_tests.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/module_tests.h" #include "ieee802_11_common.h" #include "ieee802_11_defs.h" #include "gas.h" diff --git a/src/common/ctrl_iface_common.c b/src/common/ctrl_iface_common.c new file mode 100644 index 0000000000000..ebbe6ffdb385b --- /dev/null +++ b/src/common/ctrl_iface_common.c @@ -0,0 +1,173 @@ +/* + * Common hostapd/wpa_supplicant ctrl iface code. + * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2015, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include <netdb.h> +#include <sys/un.h> + +#include "utils/common.h" +#include "ctrl_iface_common.h" + +static int sockaddr_compare(struct sockaddr_storage *a, socklen_t a_len, + struct sockaddr_storage *b, socklen_t b_len) +{ + if (a->ss_family != b->ss_family) + return 1; + + switch (a->ss_family) { +#ifdef CONFIG_CTRL_IFACE_UDP + case AF_INET: + { + struct sockaddr_in *in_a, *in_b; + + in_a = (struct sockaddr_in *) a; + in_b = (struct sockaddr_in *) b; + + if (in_a->sin_port != in_b->sin_port) + return 1; + if (in_a->sin_addr.s_addr != in_b->sin_addr.s_addr) + return 1; + break; + } + case AF_INET6: + { + struct sockaddr_in6 *in6_a, *in6_b; + + in6_a = (struct sockaddr_in6 *) a; + in6_b = (struct sockaddr_in6 *) b; + + if (in6_a->sin6_port != in6_b->sin6_port) + return 1; + if (os_memcmp(&in6_a->sin6_addr, &in6_b->sin6_addr, + sizeof(in6_a->sin6_addr)) != 0) + return 1; + break; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ +#ifdef CONFIG_CTRL_IFACE_UNIX + case AF_UNIX: + { + struct sockaddr_un *u_a, *u_b; + + u_a = (struct sockaddr_un *) a; + u_b = (struct sockaddr_un *) b; + + if (a_len != b_len || + os_memcmp(u_a->sun_path, u_b->sun_path, + a_len - offsetof(struct sockaddr_un, sun_path)) + != 0) + return 1; + break; + } +#endif /* CONFIG_CTRL_IFACE_UNIX */ + default: + return 1; + } + + return 0; +} + + +void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock, + socklen_t socklen) +{ + switch (sock->ss_family) { +#ifdef CONFIG_CTRL_IFACE_UDP + case AF_INET: + case AF_INET6: + { + char host[NI_MAXHOST] = { 0 }; + char service[NI_MAXSERV] = { 0 }; + + getnameinfo((struct sockaddr *) sock, socklen, + host, sizeof(host), + service, sizeof(service), + NI_NUMERICHOST); + + wpa_printf(level, "%s %s:%s", msg, host, service); + break; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ +#ifdef CONFIG_CTRL_IFACE_UNIX + case AF_UNIX: + { + char addr_txt[200]; + + printf_encode(addr_txt, sizeof(addr_txt), + (u8 *) ((struct sockaddr_un *) sock)->sun_path, + socklen - offsetof(struct sockaddr_un, sun_path)); + wpa_printf(level, "%s %s", msg, addr_txt); + break; + } +#endif /* CONFIG_CTRL_IFACE_UNIX */ + default: + wpa_printf(level, "%s", msg); + break; + } +} + + +int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst; + + dst = os_zalloc(sizeof(*dst)); + if (dst == NULL) + return -1; + os_memcpy(&dst->addr, from, fromlen); + dst->addrlen = fromlen; + dst->debug_level = MSG_INFO; + dl_list_add(ctrl_dst, &dst->list); + + sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor attached", from, fromlen); + return 0; +} + + +int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, + socklen_t fromlen) +{ + struct wpa_ctrl_dst *dst; + + dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { + if (!sockaddr_compare(from, fromlen, + &dst->addr, dst->addrlen)) { + sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor detached", + from, fromlen); + dl_list_del(&dst->list); + os_free(dst); + return 0; + } + } + + return -1; +} + + +int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from, + socklen_t fromlen, const char *level) +{ + struct wpa_ctrl_dst *dst; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); + + dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { + if (!sockaddr_compare(from, fromlen, + &dst->addr, dst->addrlen)) { + sockaddr_print(MSG_DEBUG, + "CTRL_IFACE changed monitor level", + from, fromlen); + dst->debug_level = atoi(level); + return 0; + } + } + + return -1; +} diff --git a/src/common/ctrl_iface_common.h b/src/common/ctrl_iface_common.h new file mode 100644 index 0000000000000..0b6e3e740291d --- /dev/null +++ b/src/common/ctrl_iface_common.h @@ -0,0 +1,38 @@ +/* + * Common hostapd/wpa_supplicant ctrl iface code. + * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2015, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +#ifndef CONTROL_IFACE_COMMON_H +#define CONTROL_IFACE_COMMON_H + +#include "utils/list.h" + +/** + * struct wpa_ctrl_dst - Data structure of control interface monitors + * + * This structure is used to store information about registered control + * interface monitors into struct wpa_supplicant. + */ +struct wpa_ctrl_dst { + struct dl_list list; + struct sockaddr_storage addr; + socklen_t addrlen; + int debug_level; + int errors; +}; + +void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock, + socklen_t socklen); + +int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, + socklen_t fromlen); +int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, + socklen_t fromlen); +int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from, + socklen_t fromlen, const char *level); + +#endif /* CONTROL_IFACE_COMMON_H */ diff --git a/src/common/defs.h b/src/common/defs.h index 6aea3751a2bc9..4f567945942e5 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -312,6 +312,7 @@ enum wpa_ctrl_req_type { WPA_CTRL_REQ_EAP_PASSPHRASE, WPA_CTRL_REQ_SIM, WPA_CTRL_REQ_PSK_PASSPHRASE, + WPA_CTRL_REQ_EXT_CERT_CHECK, NUM_WPA_CTRL_REQS }; @@ -319,13 +320,13 @@ enum wpa_ctrl_req_type { #define EAP_MAX_METHODS 8 enum mesh_plink_state { - PLINK_LISTEN = 1, - PLINK_OPEN_SENT, - PLINK_OPEN_RCVD, + PLINK_IDLE = 1, + PLINK_OPN_SNT, + PLINK_OPN_RCVD, PLINK_CNF_RCVD, PLINK_ESTAB, PLINK_HOLDING, - PLINK_BLOCKED, + PLINK_BLOCKED, /* not defined in the IEEE 802.11 standard */ }; enum set_band { @@ -334,4 +335,10 @@ enum set_band { WPA_SETBAND_2G }; +enum wpa_radio_work_band { + BAND_2_4_GHZ = BIT(0), + BAND_5_GHZ = BIT(1), + BAND_60_GHZ = BIT(2), +}; + #endif /* DEFS_H */ diff --git a/src/common/eapol_common.h b/src/common/eapol_common.h index 6958661f78b56..d773348b42c5b 100644 --- a/src/common/eapol_common.h +++ b/src/common/eapol_common.h @@ -25,7 +25,7 @@ struct ieee802_1x_hdr { struct ieee8023_hdr { u8 dest[ETH_ALEN]; u8 src[ETH_ALEN]; - u16 ethertype; + be16 ethertype; } STRUCT_PACKED; #ifdef _MSC_VER diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index d07a316a79298..b6bc449bf7dcd 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -115,6 +115,11 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->osen = pos; elems->osen_len = elen; break; + case MBO_OUI_TYPE: + /* MBO-OCE */ + elems->mbo = pos; + elems->mbo_len = elen; + break; default: wpa_printf(MSG_MSGDUMP, "Unknown WFA " "information element ignored " @@ -366,6 +371,14 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen; elems->mb_ies.nof_ies++; break; + case WLAN_EID_SUPPORTED_OPERATING_CLASSES: + elems->supp_op_classes = pos; + elems->supp_op_classes_len = elen; + break; + case WLAN_EID_RRM_ENABLED_CAPABILITIES: + elems->rrm_enabled = pos; + elems->rrm_enabled_len = elen; + break; default: unknown++; if (!show_errors) @@ -398,8 +411,8 @@ int ieee802_11_ie_count(const u8 *ies, size_t ies_len) pos = ies; end = ies + ies_len; - while (pos + 2 <= end) { - if (pos + 2 + pos[1] > end) + while (end - pos >= 2) { + if (2 + pos[1] > end - pos) break; count++; pos += 2 + pos[1]; @@ -419,8 +432,8 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, end = ies + ies_len; ie = NULL; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + 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) { @@ -441,8 +454,8 @@ 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 (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + 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) @@ -570,7 +583,8 @@ enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) { u8 op_class; - return ieee80211_freq_to_channel_ext(freq, 0, 0, &op_class, channel); + return ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT, + &op_class, channel); } @@ -579,7 +593,7 @@ enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) * for HT40 and VHT. DFS channels are not covered. * @freq: Frequency (MHz) to convert * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below - * @vht: 0 - non-VHT, 1 - 80 MHz + * @vht: VHT channel width (VHT_CHANWIDTH_*) * @op_class: Buffer for returning operating class * @channel: Buffer for returning channel number * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure @@ -588,6 +602,8 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel, int vht, u8 *op_class, u8 *channel) { + u8 vht_opclass; + /* TODO: more operating classes */ if (sec_channel > 1 || sec_channel < -1) @@ -631,17 +647,32 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, return HOSTAPD_MODE_IEEE80211A; } + switch (vht) { + case VHT_CHANWIDTH_80MHZ: + vht_opclass = 128; + break; + case VHT_CHANWIDTH_160MHZ: + vht_opclass = 129; + break; + case VHT_CHANWIDTH_80P80MHZ: + vht_opclass = 130; + break; + default: + vht_opclass = 0; + break; + } + /* 5 GHz, channels 36..48 */ if (freq >= 5180 && freq <= 5240) { if ((freq - 5000) % 5) return NUM_HOSTAPD_MODES; - if (sec_channel == 1) + if (vht_opclass) + *op_class = vht_opclass; + else if (sec_channel == 1) *op_class = 116; else if (sec_channel == -1) *op_class = 117; - else if (vht) - *op_class = 128; else *op_class = 115; @@ -650,31 +681,40 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, return HOSTAPD_MODE_IEEE80211A; } - /* 5 GHz, channels 149..161 */ - if (freq >= 5745 && freq <= 5805) { + /* 5 GHz, channels 149..169 */ + if (freq >= 5745 && freq <= 5845) { if ((freq - 5000) % 5) return NUM_HOSTAPD_MODES; - if (sec_channel == 1) + if (vht_opclass) + *op_class = vht_opclass; + else if (sec_channel == 1) *op_class = 126; else if (sec_channel == -1) *op_class = 127; - else if (vht) - *op_class = 128; - else + else if (freq <= 5805) *op_class = 124; + else + *op_class = 125; *channel = (freq - 5000) / 5; return HOSTAPD_MODE_IEEE80211A; } - /* 5 GHz, channels 149..169 */ - if (freq >= 5745 && freq <= 5845) { + /* 5 GHz, channels 100..140 */ + if (freq >= 5000 && freq <= 5700) { if ((freq - 5000) % 5) return NUM_HOSTAPD_MODES; - *op_class = 125; + if (vht_opclass) + *op_class = vht_opclass; + else if (sec_channel == 1) + *op_class = 122; + else if (sec_channel == -1) + *op_class = 123; + else + *op_class = 121; *channel = (freq - 5000) / 5; @@ -1145,3 +1185,135 @@ struct wpabuf * mb_ies_by_info(struct mb_ies_info *info) return mb_ies; } + + +const struct oper_class_map global_op_class[] = { + { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20, NO_P2P_SUPP }, + + /* Do not enable HT40 on 2.4 GHz for P2P use for now */ + { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS, NO_P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS, NO_P2P_SUPP }, + + { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 118, 52, 64, 4, BW20, NO_P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 119, 52, 60, 8, BW40PLUS, NO_P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 120, 56, 64, 8, BW40MINUS, NO_P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 121, 100, 140, 4, BW20, NO_P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 122, 100, 132, 8, BW40PLUS, NO_P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 123, 104, 136, 8, BW40MINUS, NO_P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS, P2P_SUPP }, + + /* + * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center + * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of + * 80 MHz, but currently use the following definition for simplicity + * (these center frequencies are not actual channels, which makes + * wpas_p2p_allow_channel() fail). wpas_p2p_verify_80mhz() should take + * care of removing invalid channels. + */ + { HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP }, + { HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160, P2P_SUPP }, + { -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP } +}; + + +static enum phy_type ieee80211_phy_type_by_freq(int freq) +{ + enum hostapd_hw_mode hw_mode; + u8 channel; + + hw_mode = ieee80211_freq_to_chan(freq, &channel); + + switch (hw_mode) { + case HOSTAPD_MODE_IEEE80211A: + return PHY_TYPE_OFDM; + case HOSTAPD_MODE_IEEE80211B: + return PHY_TYPE_HRDSSS; + case HOSTAPD_MODE_IEEE80211G: + return PHY_TYPE_ERP; + case HOSTAPD_MODE_IEEE80211AD: + return PHY_TYPE_DMG; + default: + return PHY_TYPE_UNSPECIFIED; + }; +} + + +/* ieee80211_get_phy_type - Derive the phy type by freq and bandwidth */ +enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht) +{ + if (vht) + return PHY_TYPE_VHT; + if (ht) + return PHY_TYPE_HT; + + return ieee80211_phy_type_by_freq(freq); +} + + +size_t global_op_class_size = ARRAY_SIZE(global_op_class); + + +/** + * get_ie - Fetch a specified information element from IEs buffer + * @ies: Information elements buffer + * @len: Information elements buffer length + * @eid: Information element identifier (WLAN_EID_*) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the IEs + * buffer or %NULL in case the element is not found. + */ +const u8 * get_ie(const u8 *ies, size_t len, u8 eid) +{ + const u8 *end; + + 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]; + } + + return NULL; +} + + +size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len) +{ + /* + * MBO IE requires 6 bytes without the attributes: EID (1), length (1), + * OUI (3), OUI type (1). + */ + if (len < 6 + attr_len) { + wpa_printf(MSG_DEBUG, + "MBO: Not enough room in buffer for MBO IE: buf len = %zu, attr_len = %zu", + len, attr_len); + return 0; + } + + *buf++ = WLAN_EID_VENDOR_SPECIFIC; + *buf++ = attr_len + 4; + WPA_PUT_BE24(buf, OUI_WFA); + buf += 3; + *buf++ = MBO_OUI_TYPE; + os_memcpy(buf, attr, attr_len); + + return 6 + attr_len; +} diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 55ce0223d9235..42f39096f86ca 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -9,6 +9,8 @@ #ifndef IEEE802_11_COMMON_H #define IEEE802_11_COMMON_H +#include "defs.h" + #define MAX_NOF_MB_IES_SUPPORTED 5 struct mb_ies_info { @@ -56,9 +58,12 @@ struct ieee802_11_elems { const u8 *bss_max_idle_period; const u8 *ssid_list; const u8 *osen; + const u8 *mbo; const u8 *ampe; const u8 *mic; const u8 *pref_freq_list; + const u8 *supp_op_classes; + const u8 *rrm_enabled; u8 ssid_len; u8 supp_rates_len; @@ -85,9 +90,13 @@ struct ieee802_11_elems { u8 ext_capab_len; u8 ssid_list_len; u8 osen_len; + u8 mbo_len; u8 ampe_len; u8 mic_len; u8 pref_freq_list_len; + u8 supp_op_classes_len; + u8 rrm_enabled_len; + struct mb_ies_info mb_ies; }; @@ -118,6 +127,7 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel, int vht, u8 *op_class, u8 *channel); int ieee80211_is_dfs(int freq); +enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht); int supp_rates_11b_only(struct ieee802_11_elems *elems); int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, @@ -125,4 +135,22 @@ int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, struct wpabuf * mb_ies_by_info(struct mb_ies_info *info); const char * fc2str(u16 fc); + +struct oper_class_map { + enum hostapd_hw_mode mode; + u8 op_class; + u8 min_chan; + u8 max_chan; + u8 inc; + enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160, BW160, BW80P80 } bw; + enum { P2P_SUPP, NO_P2P_SUPP } p2p; +}; + +extern const struct oper_class_map global_op_class[]; +extern size_t global_op_class_size; + +const u8 * get_ie(const u8 *ies, size_t len, u8 eid); + +size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len); + #endif /* IEEE802_11_COMMON_H */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 44530ce3cee6a..d453aec790add 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -94,8 +94,13 @@ #define WLAN_CAPABILITY_PBCC BIT(6) #define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7) #define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8) +#define WLAN_CAPABILITY_QOS BIT(9) #define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10) +#define WLAN_CAPABILITY_APSD BIT(11) +#define WLAN_CAPABILITY_RADIO_MEASUREMENT BIT(12) #define WLAN_CAPABILITY_DSSS_OFDM BIT(13) +#define WLAN_CAPABILITY_DELAYED_BLOCK_ACK BIT(14) +#define WLAN_CAPABILITY_IMM_BLOCK_ACK BIT(15) /* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */ #define WLAN_STATUS_SUCCESS 0 @@ -247,6 +252,7 @@ #define WLAN_EID_TIMEOUT_INTERVAL 56 #define WLAN_EID_RIC_DATA 57 #define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59 +#define WLAN_EID_EXT_CHANSWITCH_ANN 60 #define WLAN_EID_HT_OPERATION 61 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62 #define WLAN_EID_WAPI 68 @@ -360,6 +366,16 @@ /* byte 1 (out of 5) */ #define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0) #define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1) +/* byte 2 (out of 5) */ +#define WLAN_RRM_CAPS_LCI_MEASUREMENT BIT(4) +/* byte 5 (out of 5) */ +#define WLAN_RRM_CAPS_FTM_RANGE_REPORT BIT(2) + +/* + * IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 (Fine Timing Measurement Range + * request) - Minimum AP count + */ +#define WLAN_RRM_RANGE_REQ_MAX_MIN_AP 15 /* Timeout Interval Type */ #define WLAN_TIMEOUT_REASSOC_DEADLINE 1 @@ -407,7 +423,12 @@ enum anqp_info_id { ANQP_AP_LOCATION_PUBLIC_URI = 267, ANQP_DOMAIN_NAME = 268, ANQP_EMERGENCY_ALERT_URI = 269, + ANQP_TDLS_CAPABILITY = 270, ANQP_EMERGENCY_NAI = 271, + ANQP_NEIGHBOR_REPORT = 272, + ANQP_VENUE_URL = 277, + ANQP_ADVICE_OF_CHARGE = 278, + ANQP_LOCAL_CONTENT = 279, ANQP_VENDOR_SPECIFIC = 56797 }; @@ -442,6 +463,48 @@ enum nai_realm_eap_cred_type { NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10 }; +/* + * IEEE P802.11-REVmc/D5.0 Table 9-81 - Measurement type definitions for + * measurement requests + */ +enum measure_type { + MEASURE_TYPE_BASIC = 0, + MEASURE_TYPE_CCA = 1, + MEASURE_TYPE_RPI_HIST = 2, + MEASURE_TYPE_CHANNEL_LOAD = 3, + MEASURE_TYPE_NOISE_HIST = 4, + MEASURE_TYPE_BEACON = 5, + MEASURE_TYPE_FRAME = 6, + MEASURE_TYPE_STA_STATISTICS = 7, + MEASURE_TYPE_LCI = 8, + MEASURE_TYPE_TRANSMIT_STREAM = 9, + MEASURE_TYPE_MULTICAST_DIAG = 10, + MEASURE_TYPE_LOCATION_CIVIC = 11, + MEASURE_TYPE_LOCATION_ID = 12, + MEASURE_TYPE_DIRECTIONAL_CHAN_QUALITY = 13, + MEASURE_TYPE_DIRECTIONAL_MEASURE = 14, + MEASURE_TYPE_DIRECTIONAL_STATS = 15, + MEASURE_TYPE_FTM_RANGE = 16, + MEASURE_TYPE_MEASURE_PAUSE = 255, +}; + +/* IEEE Std 802.11-2012 Table 8-71 - Location subject definition */ +enum location_subject { + LOCATION_SUBJECT_LOCAL = 0, + LOCATION_SUBJECT_REMOTE = 1, + LOCATION_SUBJECT_3RD_PARTY = 2, +}; + +/* + * IEEE P802.11-REVmc/D5.0 Table 9-94 - Optional subelement IDs for LCI request + */ +enum lci_req_subelem { + LCI_REQ_SUBELEM_AZIMUTH_REQ = 1, + LCI_REQ_SUBELEM_ORIGINATOR_MAC_ADDR = 2, + LCI_REQ_SUBELEM_TARGET_MAC_ADDR = 3, + LCI_REQ_SUBELEM_MAX_AGE = 4, +}; + #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ @@ -516,10 +579,7 @@ struct ieee80211_mgmt { * FH Params, DS Params, CF Params, IBSS Params, TIM */ u8 variable[]; } STRUCT_PACKED beacon; - struct { - /* only variable items: SSID, Supported rates */ - u8 variable[0]; - } STRUCT_PACKED probe_req; + /* probe_req: only variable items: SSID, Supported rates */ struct { u8 timestamp[8]; le16 beacon_int; @@ -625,12 +685,19 @@ struct ieee80211_mgmt { u8 action; u8 variable[]; } STRUCT_PACKED fst_action; + struct { + u8 action; + u8 dialog_token; + u8 variable[]; + } STRUCT_PACKED rrm; } u; } STRUCT_PACKED action; } u; } STRUCT_PACKED; +#define IEEE80211_MAX_MMPDU_SIZE 2304 + /* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */ #define IEEE80211_HT_MCS_MASK_LEN 10 @@ -690,9 +757,14 @@ struct ieee80211_ampe_ie { u8 selected_pairwise_suite[4]; u8 local_nonce[32]; u8 peer_nonce[32]; - u8 mgtk[16]; - u8 key_rsc[8]; - u8 key_expiration[4]; + /* Followed by + * Key Replay Counter[8] (optional) + * (only in Mesh Group Key Inform/Acknowledge frames) + * GTKdata[variable] (optional) + * (MGTK[variable] || Key RSC[8] || GTKExpirationTime[4]) + * IGTKdata[variable] (optional) + * (Key ID[2], IPN[6], IGTK[variable] in IGTK KDE format) + */ } STRUCT_PACKED; #ifdef _MSC_VER @@ -879,6 +951,8 @@ struct ieee80211_ampe_ie { #define WFD_OUI_TYPE 10 #define HS20_IE_VENDOR_TYPE 0x506f9a10 #define OSEN_IE_VENDOR_TYPE 0x506f9a12 +#define MBO_IE_VENDOR_TYPE 0x506f9a16 +#define MBO_OUI_TYPE 22 #define WMM_OUI_TYPE 2 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 @@ -1021,6 +1095,95 @@ enum wmm_ac { #define HS20_DEAUTH_REASON_CODE_BSS 0 #define HS20_DEAUTH_REASON_CODE_ESS 1 +/* MBO v0.0_r19, 4.2: MBO Attributes */ +/* Table 4-5: MBO Attributes */ +enum mbo_attr_id { + MBO_ATTR_ID_AP_CAPA_IND = 1, + MBO_ATTR_ID_NON_PREF_CHAN_REPORT = 2, + MBO_ATTR_ID_CELL_DATA_CAPA = 3, + MBO_ATTR_ID_ASSOC_DISALLOW = 4, + MBO_ATTR_ID_CELL_DATA_PREF = 5, + MBO_ATTR_ID_TRANSITION_REASON = 6, + MBO_ATTR_ID_TRANSITION_REJECT_REASON = 7, + MBO_ATTR_ID_ASSOC_RETRY_DELAY = 8, +}; + +/* MBO v0.0_r19, 4.2.1: MBO AP Capability Indication Attribute */ +/* Table 4-7: MBO AP Capability Indication Field Values */ +#define MBO_AP_CAPA_CELL_AWARE BIT(6) + +/* MBO v0.0_r19, 4.2.2: Non-preferred Channel Report Attribute */ +/* Table 4-10: Reason Code Field Values */ +enum mbo_non_pref_chan_reason { + MBO_NON_PREF_CHAN_REASON_UNSPECIFIED = 0, + MBO_NON_PREF_CHAN_REASON_RSSI = 1, + MBO_NON_PREF_CHAN_REASON_EXT_INTERFERENCE = 2, + MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE = 3, +}; + +/* MBO v0.0_r19, 4.2.3: Cellular Data Capabilities Attribute */ +/* Table 4-13: Cellular Data Connectivity Field */ +enum mbo_cellular_capa { + MBO_CELL_CAPA_AVAILABLE = 1, + MBO_CELL_CAPA_NOT_AVAILABLE = 2, + MBO_CELL_CAPA_NOT_SUPPORTED = 3, +}; + +/* MBO v0.0_r19, 4.2.4: Association Disallowed Attribute */ +/* Table 4-15: Reason Code Field Values */ +enum mbo_assoc_disallow_reason { + MBO_ASSOC_DISALLOW_REASON_UNSPECIFIED = 1, + MBO_ASSOC_DISALLOW_REASON_MAX_STA = 2, + MBO_ASSOC_DISALLOW_REASON_AIR_INTERFERENCE = 3, + MBO_ASSOC_DISALLOW_REASON_AUTH_SERVER_OVERLOAD = 4, + MBO_ASSOC_DISALLOW_REASON_LOW_RSSI = 5, +}; + +/* MBO v0.0_r19, 4.2.5: Cellular Data Connection Preference Attribute */ +/* Table 4-17: Cellular Preference Field Values */ +enum mbo_cell_pref { + MBO_CELL_PREF_EXCLUDED = 0, + MBO_CELL_PREF_NO_USE = 1, + MBO_CELL_PREF_USE = 255 +}; + +/* MBO v0.0_r19, 4.2.6: Transition Reason Code Attribute */ +/* Table 4-19: Transition Reason Code Field Values */ +enum mbo_transition_reason { + MBO_TRANSITION_REASON_UNSPECIFIED = 0, + MBO_TRANSITION_REASON_FRAME_LOSS = 1, + MBO_TRANSITION_REASON_DELAY = 2, + MBO_TRANSITION_REASON_BANDWIDTH = 3, + MBO_TRANSITION_REASON_LOAD_BALANCE = 4, + MBO_TRANSITION_REASON_RSSI = 5, + MBO_TRANSITION_REASON_RETRANSMISSIONS = 6, + MBO_TRANSITION_REASON_INTERFERENCE = 7, + MBO_TRANSITION_REASON_GRAY_ZONE = 8, + MBO_TRANSITION_REASON_PREMIUM_AP = 9, +}; + +/* MBO v0.0_r19, 4.2.7: Transition Rejection Reason Code Attribute */ +/* Table 4-21: Transition Rejection Reason Code Field Values */ +enum mbo_transition_reject_reason { + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED = 0, + MBO_TRANSITION_REJECT_REASON_FRAME_LOSS = 1, + MBO_TRANSITION_REJECT_REASON_DELAY = 2, + MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY = 3, + MBO_TRANSITION_REJECT_REASON_RSSI = 4, + MBO_TRANSITION_REJECT_REASON_INTERFERENCE = 5, + 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_r25, 4.3: MBO ANQP-elements */ +#define MBO_ANQP_OUI_TYPE 0x12 +#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 1 + /* Wi-Fi Direct (P2P) */ #define P2P_OUI_TYPE 9 @@ -1178,6 +1341,14 @@ enum wifi_display_subelem { #define MESH_PATH_PROTOCOL_VENDOR 255 #define MESH_PATH_METRIC_AIRTIME 1 #define MESH_PATH_METRIC_VENDOR 255 +/* IEEE 802.11s - Mesh Capability */ +#define MESH_CAP_ACCEPT_ADDITIONAL_PEER BIT(0) +#define MESH_CAP_MCCA_SUPPORTED BIT(1) +#define MESH_CAP_MCCA_ENABLED BIT(2) +#define MESH_CAP_FORWARDING BIT(3) +#define MESH_CAP_MBCA_ENABLED BIT(4) +#define MESH_CAP_TBTT_ADJUSTING BIT(5) +#define MESH_CAP_MESH_PS_LEVEL BIT(6) enum plink_action_field { PLINK_OPEN = 1, @@ -1280,14 +1451,25 @@ enum bss_trans_mgmt_status_code { WNM_BSS_TM_REJECT_LEAVING_ESS = 8 }; +/* + * IEEE P802.11-REVmc/D5.0 Table 9-150 - Optional subelement IDs for + * neighbor report + */ #define WNM_NEIGHBOR_TSF 1 #define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING 2 #define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE 3 #define WNM_NEIGHBOR_BSS_TERMINATION_DURATION 4 #define WNM_NEIGHBOR_BEARING 5 +#define WNM_NEIGHBOR_WIDE_BW_CHAN 6 +#define WNM_NEIGHBOR_MEASUREMENT_REPORT 39 +#define WNM_NEIGHBOR_HT_CAPAB 45 +#define WNM_NEIGHBOR_HT_OPER 61 +#define WNM_NEIGHBOR_SEC_CHAN_OFFSET 62 #define WNM_NEIGHBOR_MEASUREMENT_PILOT 66 #define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70 #define WNM_NEIGHBOR_MULTIPLE_BSSID 71 +#define WNM_NEIGHBOR_VHT_CAPAB 191 +#define WNM_NEIGHBOR_VHT_OPER 192 /* QoS action */ enum qos_action { @@ -1356,6 +1538,8 @@ struct tpc_report { u8 link_margin; } STRUCT_PACKED; +#define RRM_CAPABILITIES_IE_LEN 5 + /* IEEE Std 802.11-2012, 8.5.7.4 - Link Measurement Request frame format */ struct rrm_link_measurement_request { u8 dialog_token; @@ -1375,8 +1559,6 @@ struct rrm_link_measurement_report { u8 variable[0]; } STRUCT_PACKED; -#define SSID_MAX_LEN 32 - /* IEEE Std 802.11ad-2012 - Multi-band element */ struct multi_band_ie { u8 eid; /* WLAN_EID_MULTI_BAND */ @@ -1433,4 +1615,49 @@ enum fst_action { FST_ACTION_ON_CHANNEL_TUNNEL = 5, }; +/* IEEE Std 802.11ac-2013, Annex C - dot11PHYType */ +enum phy_type { + PHY_TYPE_UNSPECIFIED = 0, + PHY_TYPE_FHSS = 1, + PHY_TYPE_DSSS = 2, + PHY_TYPE_IRBASEBAND = 3, + PHY_TYPE_OFDM = 4, + PHY_TYPE_HRDSSS = 5, + PHY_TYPE_ERP = 6, + PHY_TYPE_HT = 7, + PHY_TYPE_DMG = 8, + PHY_TYPE_VHT = 9, +}; + +/* IEEE P802.11-REVmc/D5.0, 9.4.2.37 - Neighbor Report element */ +/* BSSID Information Field */ +#define NEI_REP_BSSID_INFO_AP_NOT_REACH BIT(0) +#define NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH BIT(1) +#define NEI_REP_BSSID_INFO_AP_REACHABLE (BIT(0) | BIT(1)) +#define NEI_REP_BSSID_INFO_SECURITY BIT(2) +#define NEI_REP_BSSID_INFO_KEY_SCOPE BIT(3) +#define NEI_REP_BSSID_INFO_SPECTRUM_MGMT BIT(4) +#define NEI_REP_BSSID_INFO_QOS BIT(5) +#define NEI_REP_BSSID_INFO_APSD BIT(6) +#define NEI_REP_BSSID_INFO_RM BIT(7) +#define NEI_REP_BSSID_INFO_DELAYED_BA BIT(8) +#define NEI_REP_BSSID_INFO_IMM_BA BIT(9) +#define NEI_REP_BSSID_INFO_MOBILITY_DOMAIN BIT(10) +#define NEI_REP_BSSID_INFO_HT BIT(11) +#define NEI_REP_BSSID_INFO_VHT BIT(12) +#define NEI_REP_BSSID_INFO_FTM BIT(13) + +/* + * IEEE P802.11-REVmc/D5.0 Table 9-152 - HT/VHT Operation Information + * subfields. + * Note: These definitions are not the same as other VHT_CHANWIDTH_*. + */ +enum nr_chan_width { + NR_CHAN_WIDTH_20 = 0, + NR_CHAN_WIDTH_40 = 1, + NR_CHAN_WIDTH_80 = 2, + NR_CHAN_WIDTH_160 = 3, + NR_CHAN_WIDTH_80P80 = 4, +}; + #endif /* IEEE802_11_DEFS_H */ diff --git a/src/common/ieee802_1x_defs.h b/src/common/ieee802_1x_defs.h index cc88caa8d2f33..a0c1d1bfafc4c 100644 --- a/src/common/ieee802_1x_defs.h +++ b/src/common/ieee802_1x_defs.h @@ -10,7 +10,7 @@ #define IEEE802_1X_DEFS_H #define CS_ID_LEN 8 -#define CS_ID_GCM_AES_128 {0x00, 0x80, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01} +#define CS_ID_GCM_AES_128 0x0080020001000001ULL #define CS_NAME_GCM_AES_128 "GCM-AES-128" enum macsec_policy { diff --git a/src/common/linux_bridge.h b/src/common/linux_bridge.h new file mode 100644 index 0000000000000..7b768464fb542 --- /dev/null +++ b/src/common/linux_bridge.h @@ -0,0 +1,24 @@ +/* + * Linux bridge configuration kernel interface + * Copyright (c) 2016, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef LINUX_BRIDGE_H +#define LINUX_BRIDGE_H + +/* This interface is defined in linux/if_bridge.h */ + +#define BRCTL_GET_VERSION 0 +#define BRCTL_GET_BRIDGES 1 +#define BRCTL_ADD_BRIDGE 2 +#define BRCTL_DEL_BRIDGE 3 +#define BRCTL_ADD_IF 4 +#define BRCTL_DEL_IF 5 +#define BRCTL_GET_BRIDGE_INFO 6 +#define BRCTL_GET_PORT_LIST 7 +#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8 + +#endif /* LINUX_BRIDGE_H */ diff --git a/src/common/linux_vlan.h b/src/common/linux_vlan.h new file mode 100644 index 0000000000000..8a1dd6e466406 --- /dev/null +++ b/src/common/linux_vlan.h @@ -0,0 +1,52 @@ +/* + * Linux VLAN configuration kernel interface + * Copyright (c) 2016, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef LINUX_VLAN_H +#define LINUX_VLAN_H + +/* This ioctl is defined in linux/sockios.h */ + +#ifndef SIOCSIFVLAN +#define SIOCSIFVLAN 0x8983 +#endif /* SIOCSIFVLAN */ + +/* This interface is defined in linux/if_vlan.h */ + +#define ADD_VLAN_CMD 0 +#define DEL_VLAN_CMD 1 +#define SET_VLAN_INGRESS_PRIORITY_CMD 2 +#define SET_VLAN_EGRESS_PRIORITY_CMD 3 +#define GET_VLAN_INGRESS_PRIORITY_CMD 4 +#define GET_VLAN_EGRESS_PRIORITY_CMD 5 +#define SET_VLAN_NAME_TYPE_CMD 6 +#define SET_VLAN_FLAG_CMD 7 +#define GET_VLAN_REALDEV_NAME_CMD 8 +#define GET_VLAN_VID_CMD 9 + +#define VLAN_NAME_TYPE_PLUS_VID 0 +#define VLAN_NAME_TYPE_RAW_PLUS_VID 1 +#define VLAN_NAME_TYPE_PLUS_VID_NO_PAD 2 +#define VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD 3 + +struct vlan_ioctl_args { + int cmd; + char device1[24]; + + union { + char device2[24]; + int VID; + unsigned int skb_priority; + unsigned int name_type; + unsigned int bind_type; + unsigned int flag; + } u; + + short vlan_qos; +}; + +#endif /* LINUX_VLAN_H */ diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h index 28985f5194e21..adaec890b58de 100644 --- a/src/common/qca-vendor.h +++ b/src/common/qca-vendor.h @@ -89,6 +89,102 @@ enum qca_radiotap_vendor_ids { * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: Event used by driver, * which supports DFS offloading, to indicate a radar pattern has been * detected. The channel is now unusable. + * + * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START: Command used to + * start the P2P Listen offload function in device and pass the listen + * channel, period, interval, count, device types, and vendor specific + * information elements to the device driver and firmware. + * Uses the attributes defines in + * enum qca_wlan_vendor_attr_p2p_listen_offload. + * + * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP: Command/event used to + * indicate stop request/response of the P2P Listen offload function in + * device. As an event, it indicates either the feature stopped after it + * was already running or feature has actually failed to start. Uses the + * attributes defines in enum qca_wlan_vendor_attr_p2p_listen_offload. + * + * @QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH: After AP starts + * beaconing, this sub command provides the driver, the frequencies on the + * 5 GHz band to check for any radar activity. Driver selects one channel + * from this priority list provided through + * @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST and starts + * to check for radar activity on it. If no radar activity is detected + * during the channel availability check period, driver internally switches + * to the selected frequency of operation. If the frequency is zero, driver + * internally selects a channel. The status of this conditional switch is + * indicated through an event using the same sub command through + * @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS. Attributes are + * listed in qca_wlan_vendor_attr_sap_conditional_chan_switch. + * + * @QCA_NL80211_VENDOR_SUBCMD_GPIO_CONFIG_COMMAND: Set GPIO pins. This uses the + * attributes defined in enum qca_wlan_gpio_attr. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_HW_CAPABILITY: Fetch hardware capabilities. + * This uses @QCA_WLAN_VENDOR_ATTR_GET_HW_CAPABILITY to indicate which + * capabilities are to be fetched and other + * enum qca_wlan_vendor_attr_get_hw_capability attributes to return the + * requested capabilities. + * + * @QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT: Link layer statistics extension. + * enum qca_wlan_vendor_attr_ll_stats_ext attributes are used with this + * command and event. + * + * @QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA: Get capabilities for + * indoor location features. Capabilities are reported in + * QCA_WLAN_VENDOR_ATTR_LOC_CAPA. + * + * @QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION: Start an FTM + * (fine timing measurement) session with one or more peers. + * Specify Session cookie in QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE and + * peer information in QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS. + * On success, 0 or more QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT + * events will be reported, followed by + * QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE event to indicate + * end of session. + * Refer to IEEE P802.11-REVmc/D7.0, 11.24.6 + * + * @QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION: Abort a running session. + * A QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE will be reported with + * status code indicating session was aborted. + * + * @QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT: Event with measurement + * results for one peer. Results are reported in + * QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEER_RESULTS. + * + * @QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE: Event triggered when + * FTM session is finished, either successfully or aborted by + * request. + * + * @QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER: Configure FTM responder + * mode. QCA_WLAN_VENDOR_ATTR_FTM_RESPONDER_ENABLE specifies whether + * to enable or disable the responder. LCI/LCR reports can be + * configured with QCA_WLAN_VENDOR_ATTR_FTM_LCI and + * QCA_WLAN_VENDOR_ATTR_FTM_LCR. Can be called multiple + * times to update the LCI/LCR reports. + * + * @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS: Perform a standalone AOA (angle of + * arrival) measurement with a single peer. Specify peer MAC address in + * QCA_WLAN_VENDOR_ATTR_MAC_ADDR and measurement type in + * QCA_WLAN_VENDOR_ATTR_AOA_TYPE. Measurement result is reported in + * QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT event. + * + * @QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS: Abort an AOA measurement. Specify + * peer MAC address in QCA_WLAN_VENDOR_ATTR_MAC_ADDR. + * + * @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT: Event that reports + * the AOA measurement result. + * Peer MAC address reported in QCA_WLAN_VENDOR_ATTR_MAC_ADDR. + * success/failure status is reported in + * QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS. + * Measurement data is reported in QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT. + * The antenna array(s) used in the measurement are reported in + * QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK. + * + * @QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST: Encrypt/decrypt the given + * data as per the given parameters. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI: Get antenna RSSI value for a + * specific chain. */ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, @@ -140,7 +236,11 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED = 58, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED = 59, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60, - /* 61-90 - reserved for QCA */ + /* 61-73 - reserved for QCA */ + /* Wi-Fi configuration subcommands */ + QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION = 74, + QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION = 75, + /* 76-90 - reserved for QCA */ QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91, QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92, QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93, @@ -156,6 +256,35 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103, QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104, QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105, + QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN = 106, + QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE = 107, + QCA_NL80211_VENDOR_SUBCMD_OTA_TEST = 108, + QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE = 109, + /* 110..114 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB = 115, + /* 116..117 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_SET_SAP_CONFIG = 118, + QCA_NL80211_VENDOR_SUBCMD_TSF = 119, + QCA_NL80211_VENDOR_SUBCMD_WISA = 120, + /* 121 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START = 122, + QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP = 123, + QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH = 124, + QCA_NL80211_VENDOR_SUBCMD_GPIO_CONFIG_COMMAND = 125, + QCA_NL80211_VENDOR_SUBCMD_GET_HW_CAPABILITY = 126, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT = 127, + /* FTM/indoor location subcommands */ + QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA = 128, + QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION = 129, + QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION = 130, + QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT = 131, + QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE = 132, + QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER = 133, + QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS = 134, + QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS = 135, + QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136, + QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST = 137, + QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI = 138, }; @@ -185,6 +314,84 @@ enum qca_wlan_vendor_attr { QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11, /* Unsigned 32-bit value from enum qca_set_band. */ QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12, + /* Dummy (NOP) attribute for 64 bit padding */ + QCA_WLAN_VENDOR_ATTR_PAD = 13, + /* Unique FTM session cookie (Unsigned 64 bit). Specified in + * QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION. Reported in + * the session in QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT and + * QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE. + */ + QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE = 14, + /* Indoor location capabilities, returned by + * QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA. + * see enum qca_wlan_vendor_attr_loc_capa. + */ + QCA_WLAN_VENDOR_ATTR_LOC_CAPA = 15, + /* Array of nested attributes containing information about each peer + * in FTM measurement session. See enum qca_wlan_vendor_attr_peer_info + * for supported attributes for each peer. + */ + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS = 16, + /* Array of nested attributes containing measurement results for + * one or more peers, reported by the + * QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT event. + * See enum qca_wlan_vendor_attr_peer_result for list of supported + * attributes. + */ + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEER_RESULTS = 17, + /* Flag attribute for enabling or disabling responder functionality. */ + QCA_WLAN_VENDOR_ATTR_FTM_RESPONDER_ENABLE = 18, + /* Used in the QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER + * command to specify the LCI report that will be sent by + * the responder during a measurement exchange. The format is + * defined in IEEE P802.11-REVmc/D7.0, 9.4.2.22.10. + */ + QCA_WLAN_VENDOR_ATTR_FTM_LCI = 19, + /* Used in the QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER + * command to specify the location civic report that will + * be sent by the responder during a measurement exchange. + * The format is defined in IEEE P802.11-REVmc/D7.0, 9.4.2.22.13. + */ + QCA_WLAN_VENDOR_ATTR_FTM_LCR = 20, + /* Session/measurement completion status code, + * reported in QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE and + * QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT + * see enum qca_vendor_attr_loc_session_status. + */ + QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS = 21, + /* Initial dialog token used by responder (0 if not specified), + * unsigned 8 bit value. + */ + QCA_WLAN_VENDOR_ATTR_FTM_INITIAL_TOKEN = 22, + /* AOA measurement type. Requested in QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS + * and optionally in QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION if + * AOA measurements are needed as part of an FTM session. + * Reported by QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT. See + * enum qca_wlan_vendor_attr_aoa_type. + */ + QCA_WLAN_VENDOR_ATTR_AOA_TYPE = 23, + /* A bit mask (unsigned 32 bit value) of antenna arrays used + * by indoor location measurements. Refers to the antenna + * arrays described by QCA_VENDOR_ATTR_LOC_CAPA_ANTENNA_ARRAYS. + */ + QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK = 24, + /* AOA measurement data. Its contents depends on the AOA measurement + * type and antenna array mask: + * QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE: array of U16 values, + * phase of the strongest CIR path for each antenna in the measured + * array(s). + * QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP: array of 2 U16 + * values, phase and amplitude of the strongest CIR path for each + * antenna in the measured array(s). + */ + QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT = 25, + /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command + * to specify the chain number (unsigned 32 bit value) to inquire + * the corresponding antenna RSSI value */ + QCA_WLAN_VENDOR_ATTR_CHAIN_INDEX = 26, + /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command + * to report the specific antenna RSSI value (unsigned 32 bit value) */ + QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI = 27, /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1, @@ -205,12 +412,50 @@ enum qca_wlan_vendor_attr_roam_auth { QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS, /* keep last */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX = QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1 }; +enum qca_wlan_vendor_attr_p2p_listen_offload { + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INVALID = 0, + /* A 32-bit unsigned value; the P2P listen frequency (MHz); must be one + * of the social channels. + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL, + /* A 32-bit unsigned value; the P2P listen offload period (ms). + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD, + /* A 32-bit unsigned value; the P2P listen interval duration (ms). + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL, + /* A 32-bit unsigned value; number of interval times the firmware needs + * to run the offloaded P2P listen operation before it stops. + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT, + /* An array of arbitrary binary data with one or more 8-byte values. + * The device types include both primary and secondary device types. + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES, + /* An array of unsigned 8-bit characters; vendor information elements. + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE, + /* A 32-bit unsigned value; a control flag to indicate whether listen + * results need to be flushed to wpa_supplicant. + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG, + /* A 8-bit unsigned value; reason code for P2P listen offload stop + * event. + */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX = + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST - 1 +}; + enum qca_wlan_vendor_attr_acs_offload { QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0, QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL, @@ -247,11 +492,21 @@ enum qca_wlan_vendor_acs_hw_mode { * after roaming, rather than having the user space wpa_supplicant do it. * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic * band selection based on channel selection results. + * @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports + * simultaneous off-channel operations. + * @QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD: Device supports P2P + * Listen offload; a mechanism where the station's firmware takes care of + * responding to incoming Probe Request frames received from other P2P + * Devices whilst in Listen state, rather than having the user space + * wpa_supplicant do it. Information from received P2P requests are + * forwarded from firmware to host whenever the host processor wakes up. * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits */ enum qca_wlan_vendor_features { QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0, QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1, + QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2, + QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD = 3, NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ }; @@ -324,6 +579,67 @@ enum qca_set_band { QCA_SETBAND_2G, }; +/** + * enum qca_access_policy - Access control policy + * + * Access control policy is applied on the configured IE + * (QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE). + * To be set with QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY. + * + * @QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED: Deny Wi-Fi connections which match + * the specific configuration (IE) set, i.e., allow all the + * connections which do not match the configuration. + * @QCA_ACCESS_POLICY_DENY_UNLESS_LISTED: Accept Wi-Fi connections which match + * the specific configuration (IE) set, i.e., deny all the + * connections which do not match the configuration. + */ +enum qca_access_policy { + QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED, + QCA_ACCESS_POLICY_DENY_UNLESS_LISTED, +}; + +/** + * enum qca_vendor_attr_get_tsf: Vendor attributes for TSF capture + * @QCA_WLAN_VENDOR_ATTR_TSF_CMD: enum qca_tsf_operation (u32) + * @QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE: Unsigned 64 bit TSF timer value + * @QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE: Unsigned 64 bit Synchronized + * SOC timer value at TSF capture + */ +enum qca_vendor_attr_tsf_cmd { + QCA_WLAN_VENDOR_ATTR_TSF_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TSF_CMD, + QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE, + QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE, + QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TSF_MAX = + QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST - 1 +}; + +/** + * enum qca_tsf_operation: TSF driver commands + * @QCA_TSF_CAPTURE: Initiate TSF Capture + * @QCA_TSF_GET: Get TSF capture value + * @QCA_TSF_SYNC_GET: Initiate TSF capture and return with captured value + */ +enum qca_tsf_cmd { + QCA_TSF_CAPTURE, + QCA_TSF_GET, + QCA_TSF_SYNC_GET, +}; + +/** + * enum qca_vendor_attr_wisa_cmd + * @QCA_WLAN_VENDOR_ATTR_WISA_MODE: WISA mode value (u32) + * WISA setup vendor commands + */ +enum qca_vendor_attr_wisa_cmd { + QCA_WLAN_VENDOR_ATTR_WISA_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_WISA_MODE, + QCA_WLAN_VENDOR_ATTR_WISA_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_WISA_MAX = + QCA_WLAN_VENDOR_ATTR_WISA_AFTER_LAST - 1 +}; + /* IEEE 802.11 Vendor Specific elements */ /** @@ -349,9 +665,926 @@ enum qca_set_band { * * This vendor element may be included in GO Negotiation Request, P2P * Invitation Request, and Provision Discovery Request frames. + * + * @QCA_VENDOR_ELEM_HE_CAPAB: HE Capabilities element. + * This element can be used for pre-standard publication testing of HE + * before P802.11ax draft assigns the element ID. The payload of this + * vendor specific element is defined by the latest P802.11ax draft. + * Please note that the draft is still work in progress and this element + * payload is subject to change. + * + * @QCA_VENDOR_ELEM_HE_OPER: HE Operation element. + * This element can be used for pre-standard publication testing of HE + * before P802.11ax draft assigns the element ID. The payload of this + * vendor specific element is defined by the latest P802.11ax draft. + * Please note that the draft is still work in progress and this element + * payload is subject to change. */ enum qca_vendor_element_id { QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0, + QCA_VENDOR_ELEM_HE_CAPAB = 1, + QCA_VENDOR_ELEM_HE_OPER = 2, +}; + +/** + * enum qca_wlan_vendor_attr_scan - Specifies vendor scan attributes + * + * @QCA_WLAN_VENDOR_ATTR_SCAN_IE: IEs that should be included as part of scan + * @QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES: Nested unsigned 32-bit attributes + * with frequencies to be scanned (in MHz) + * @QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS: Nested attribute with SSIDs to be scanned + * @QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES: Nested array attribute of supported + * rates to be included + * @QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE: flag used to send probe requests + * at non CCK rate in 2GHz band + * @QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS: Unsigned 32-bit scan flags + * @QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE: Unsigned 64-bit cookie provided by the + * driver for the specific scan request + * @QCA_WLAN_VENDOR_ATTR_SCAN_STATUS: Unsigned 8-bit status of the scan + * request decoded as in enum scan_status + * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC: 6-byte MAC address to use when randomisation + * scan flag is set + * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK: 6-byte MAC address mask to be used with + * randomisation + */ +enum qca_wlan_vendor_attr_scan { + QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0, + QCA_WLAN_VENDOR_ATTR_SCAN_IE, + QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES, + QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS, + QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES, + QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE, + QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS, + QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, + QCA_WLAN_VENDOR_ATTR_SCAN_STATUS, + QCA_WLAN_VENDOR_ATTR_SCAN_MAC, + QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK, + QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SCAN_MAX = + QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1 +}; + +/** + * enum scan_status - Specifies the valid values the vendor scan attribute + * QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take + * + * @VENDOR_SCAN_STATUS_NEW_RESULTS: implies the vendor scan is successful with + * new scan results + * @VENDOR_SCAN_STATUS_ABORTED: implies the vendor scan was aborted in-between + */ +enum scan_status { + VENDOR_SCAN_STATUS_NEW_RESULTS, + VENDOR_SCAN_STATUS_ABORTED, + VENDOR_SCAN_STATUS_MAX, +}; + +/** + * enum qca_vendor_attr_ota_test - Specifies the values for vendor + * command QCA_NL80211_VENDOR_SUBCMD_OTA_TEST + * @QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE: enable ota test + */ +enum qca_vendor_attr_ota_test { + QCA_WLAN_VENDOR_ATTR_OTA_TEST_INVALID, + /* 8-bit unsigned value to indicate if OTA test is enabled */ + QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OTA_TEST_MAX = + QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST - 1 +}; + +/** + * enum qca_vendor_attr_txpower_scale - vendor sub commands index + * + * @QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE: scaling value + */ +enum qca_vendor_attr_txpower_scale { + QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_INVALID, + /* 8-bit unsigned value to indicate the scaling of tx power */ + QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX = + QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST - 1 +}; + +/** + * enum qca_vendor_attr_txpower_decr_db - Attributes for TX power decrease + * + * These attributes are used with QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB. + */ +enum qca_vendor_attr_txpower_decr_db { + QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_INVALID, + /* 8-bit unsigned value to indicate the reduction of TX power in dB for + * a virtual interface. */ + QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_MAX = + QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST - 1 +}; + +/* Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION and + * QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION subcommands. + */ +enum qca_wlan_vendor_attr_config { + QCA_WLAN_VENDOR_ATTR_CONFIG_INVALID = 0, + /* Unsigned 32-bit value to set the DTIM period. + * Whether the wifi chipset wakes at every dtim beacon or a multiple of + * the DTIM period. If DTIM is set to 3, the STA shall wake up every 3 + * DTIM beacons. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_DTIM = 1, + /* Unsigned 32-bit value to set the wifi_iface stats averaging factor + * used to calculate statistics like average the TSF offset or average + * number of frame leaked. + * For instance, upon Beacon frame reception: + * current_avg = ((beacon_TSF - TBTT) * factor + previous_avg * (0x10000 - factor) ) / 0x10000 + * For instance, when evaluating leaky APs: + * current_avg = ((num frame received within guard time) * factor + previous_avg * (0x10000 - factor)) / 0x10000 + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR = 2, + /* Unsigned 32-bit value to configure guard time, i.e., when + * implementing IEEE power management based on frame control PM bit, how + * long the driver waits before shutting down the radio and after + * receiving an ACK frame for a Data frame with PM bit set. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME = 3, + /* Unsigned 32-bit value to change the FTM capability dynamically */ + QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT = 4, + /* Unsigned 16-bit value to configure maximum TX rate dynamically */ + QCA_WLAN_VENDOR_ATTR_CONF_TX_RATE = 5, + /* Unsigned 32-bit value to configure the number of continuous + * Beacon Miss which shall be used by the firmware to penalize + * the RSSI. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS = 6, + /* Unsigned 8-bit value to configure the channel avoidance indication + * behavior. Firmware to send only one indication and ignore duplicate + * indications when set to avoid multiple Apps wakeups. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND = 7, + /* 8-bit unsigned value to configure the maximum TX MPDU for + * aggregation. */ + QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION = 8, + /* 8-bit unsigned value to configure the maximum RX MPDU for + * aggregation. */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION = 9, + /* 8-bit unsigned value to configure the Non aggregrate/11g sw + * retry threshold (0 disable, 31 max). */ + QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY = 10, + /* 8-bit unsigned value to configure the aggregrate sw + * retry threshold (0 disable, 31 max). */ + QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY = 11, + /* 8-bit unsigned value to configure the MGMT frame + * retry threshold (0 disable, 31 max). */ + QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY = 12, + /* 8-bit unsigned value to configure the CTRL frame + * retry threshold (0 disable, 31 max). */ + QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY = 13, + /* 8-bit unsigned value to configure the propagation delay for + * 2G/5G band (0~63, units in us) */ + QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY = 14, + /* Unsigned 32-bit value to configure the number of unicast TX fail + * packet count. The peer is disconnected once this threshold is + * reached. */ + QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT = 15, + /* Attribute used to set scan default IEs to the driver. + * + * These IEs can be used by scan operations that will be initiated by + * the driver/firmware. + * + * For further scan requests coming to the driver, these IEs should be + * merged with the IEs received along with scan request coming to the + * driver. If a particular IE is present in the scan default IEs but not + * present in the scan request, then that IE should be added to the IEs + * sent in the Probe Request frames for that scan request. */ + QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES = 16, + /* Unsigned 32-bit attribute for generic commands */ + QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_COMMAND = 17, + /* Unsigned 32-bit value attribute for generic commands */ + QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_VALUE = 18, + /* Unsigned 32-bit data attribute for generic command response */ + QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA = 19, + /* Unsigned 32-bit length attribute for + * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */ + QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_LENGTH = 20, + /* Unsigned 32-bit flags attribute for + * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */ + QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_FLAGS = 21, + /* Unsigned 32-bit, defining the access policy. + * See enum qca_access_policy. Used with + * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST. */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY = 22, + /* Sets the list of full set of IEs for which a specific access policy + * has to be applied. Used along with + * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY to control the access. + * Zero length payload can be used to clear this access constraint. */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST = 23, + /* Unsigned 32-bit, specifies the interface index (netdev) for which the + * corresponding configurations are applied. If the interface index is + * not specified, the configurations are attributed to the respective + * wiphy. */ + QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX = 24, + /* 8-bit unsigned value to trigger QPower: 1-Enable, 0-Disable */ + QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER = 25, + /* 8-bit unsigned value to configure the driver and below layers to + * ignore the assoc disallowed set by APs while connecting + * 1-Ignore, 0-Don't ignore */ + QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED = 26, + /* 32-bit unsigned value to trigger antenna diversity features: + * 1-Enable, 0-Disable */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA = 27, + /* 32-bit unsigned value to configure specific chain antenna */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN = 28, + /* 32-bit unsigned value to trigger cycle selftest + * 1-Enable, 0-Disable */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST = 29, + /* 32-bit unsigned to configure the cycle time of selftest + * the unit is micro-second */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL = 30, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_sap_config - Parameters for AP configuration + */ +enum qca_wlan_vendor_attr_sap_config { + QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_INVALID = 0, + /* 1 - reserved for QCA */ + /* List of frequencies on which AP is expected to operate. + * This is irrespective of ACS configuration. This list is a priority + * based one and is looked for before the AP is created to ensure the + * best concurrency sessions (avoid MCC and use DBS/SCC) co-exist in + * the system. + */ + QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST = 2, + + QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_sap_conditional_chan_switch - Parameters for AP + * conditional channel switch + */ +enum qca_wlan_vendor_attr_sap_conditional_chan_switch { + QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_INVALID = 0, + /* Priority based frequency list (an array of u32 values in host byte + * order) */ + QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST = 1, + /* Status of the conditional switch (u32). + * 0: Success, Non-zero: Failure + */ + QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS = 2, + + QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX = + QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_gpio_attr - Parameters for GPIO configuration + */ +enum qca_wlan_gpio_attr { + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INVALID = 0, + /* Unsigned 32-bit attribute for GPIO command */ + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND, + /* Unsigned 32-bit attribute for GPIO PIN number to configure */ + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM, + /* Unsigned 32-bit attribute for GPIO value to configure */ + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE, + /* Unsigned 32-bit attribute for GPIO pull type */ + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE, + /* Unsigned 32-bit attribute for GPIO interrupt mode */ + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_LAST, + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MAX = + QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_get_hw_capability - Wi-Fi hardware capability + */ +enum qca_wlan_vendor_attr_get_hw_capability { + QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_INVALID, + /* Antenna isolation + * An attribute used in the response. + * The content of this attribute is encoded in a byte array. Each byte + * value is an antenna isolation value. The array length is the number + * of antennas. + */ + QCA_WLAN_VENDOR_ATTR_ANTENNA_ISOLATION, + /* Request HW capability + * An attribute used in the request. + * The content of this attribute is a u32 array for one or more of + * hardware capabilities (attribute IDs) that are being requested. Each + * u32 value has a value from this + * enum qca_wlan_vendor_attr_get_hw_capability + * identifying which capabilities are requested. + */ + QCA_WLAN_VENDOR_ATTR_GET_HW_CAPABILITY, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_MAX = + QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_ll_stats_ext - Attributes for MAC layer monitoring + * offload which is an extension for LL_STATS. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD: Monitoring period. Unit in ms. + * If MAC counters do not exceed the threshold, FW will report monitored + * link layer counters periodically as this setting. The first report is + * always triggered by this timer. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD: It is a percentage (1-99). + * For each MAC layer counter, FW holds two copies. One is the current value. + * The other is the last report. Once a current counter's increment is larger + * than the threshold, FW will indicate that counter to host even if the + * monitoring timer does not expire. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_CHG: Peer STA power state change + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TID: TID of MSDU + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NUM_MSDU: Count of MSDU with the same + * failure code. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS: TX failure code + * 1: TX packet discarded + * 2: No ACK + * 3: Postpone + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS: peer MAC address + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_STATE: Peer STA current state + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL: Global threshold. + * Threshold for all monitored parameters. If per counter dedicated threshold + * is not enabled, this threshold will take effect. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_EVENT_MODE: Indicate what triggers this + * event, PERORID_TIMEOUT == 1, THRESH_EXCEED == 0. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID: interface ID + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ID: peer ID + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP: bitmap for TX counters + * Bit0: TX counter unit in MSDU + * Bit1: TX counter unit in MPDU + * Bit2: TX counter unit in PPDU + * Bit3: TX counter unit in byte + * Bit4: Dropped MSDUs + * Bit5: Dropped Bytes + * Bit6: MPDU retry counter + * Bit7: MPDU failure counter + * Bit8: PPDU failure counter + * Bit9: MPDU aggregation counter + * Bit10: MCS counter for ACKed MPDUs + * Bit11: MCS counter for Failed MPDUs + * Bit12: TX Delay counter + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP: bitmap for RX counters + * Bit0: MAC RX counter unit in MPDU + * Bit1: MAC RX counter unit in byte + * Bit2: PHY RX counter unit in PPDU + * Bit3: PHY RX counter unit in byte + * Bit4: Disorder counter + * Bit5: Retry counter + * Bit6: Duplication counter + * Bit7: Discard counter + * Bit8: MPDU aggregation size counter + * Bit9: MCS counter + * Bit10: Peer STA power state change (wake to sleep) counter + * Bit11: Peer STA power save counter, total time in PS mode + * Bit12: Probe request counter + * Bit13: Other management frames counter + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP: bitmap for CCA + * Bit0: Idle time + * Bit1: TX time + * Bit2: time RX in current bss + * Bit3: Out of current bss time + * Bit4: Wireless medium busy time + * Bit5: RX in bad condition time + * Bit6: TX in bad condition time + * Bit7: time wlan card not available + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP: bitmap for signal + * Bit0: Per channel SNR counter + * Bit1: Per channel noise floor counter + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM: number of peers + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CHANNEL_NUM: number of channels + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_AC_RX_NUM: number of RX stats + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS: per channel BSS CCA stats + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER: container for per PEER stats + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU: Number of total TX MSDUs + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU: Number of total TX MPDUs + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU: Number of total TX PPDUs + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES: bytes of TX data + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP: Number of dropped TX packets + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES: Bytes dropped + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY: waiting time without an ACK + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK: number of MPDU not-ACKed + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK: number of PPDU not-ACKed + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR_NUM: + * aggregation stats buffer length + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS_NUM: length of mcs stats + * buffer for ACKed MPDUs. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS_NUM: length of mcs stats + * buffer for failed MPDUs. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_DELAY_ARRAY_SIZE: + * length of delay stats array. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR: TX aggregation stats + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS: MCS stats for ACKed MPDUs + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS: MCS stats for failed MPDUs + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY: tx delay stats + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU: MPDUs received + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES: bytes received + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU: PPDU received + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES: PPDU bytes received + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST: packets lost + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY: number of RX packets + * flagged as retransmissions + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP: number of RX packets + * flagged as duplicated + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD: number of RX + * packets discarded + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR_NUM: length of RX aggregation + * stats buffer. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS_NUM: length of RX mcs + * stats buffer. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS: RX mcs stats buffer + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR: aggregation stats buffer + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES: times STAs go to sleep + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION: STAs' total sleep time + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ: number of probe + * requests received + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT: number of other mgmt + * frames received + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME: Percentage of idle time + * there is no TX, nor RX, nor interference. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME: percentage of time + * transmitting packets. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_TIME: percentage of time + * for receiving. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY: percentage of time + * interference detected. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD: percentage of time + * receiving packets with errors. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD: percentage of time + * TX no-ACK. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL: percentage of time + * the chip is unable to work in normal conditions. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME: percentage of time + * receiving packets in current BSS. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME: percentage of time + * receiving packets not in current BSS. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ANT_NUM: number of antennas + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_SIGNAL: + * This is a container for per antenna signal stats. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR: per antenna SNR value + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF: per antenna NF value + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON: RSSI of beacon + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON: SNR of beacon + */ +enum qca_wlan_vendor_attr_ll_stats_ext { + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_INVALID = 0, + + /* Attributes for configurations */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD, + QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD, + + /* Peer STA power state change */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_CHG, + + /* TX failure event */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TID, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NUM_MSDU, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS, + + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_STATE, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS, + + /* MAC counters */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_EVENT_MODE, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ID, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CHANNEL_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER, + + /* Sub-attributes for PEER_AC_TX */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_DELAY_ARRAY_SIZE, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY, + + /* Sub-attributes for PEER_AC_RX */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT, + + /* Sub-attributes for CCA_BSS */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_TIME, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL, + + /* sub-attribute for BSS_RX_TIME */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME, + + /* Sub-attributes for PEER_SIGNAL */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ANT_NUM, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_SIGNAL, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF, + + /* Sub-attributes for IFACE_BSS */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON, + + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX = + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST - 1 +}; + +/* Attributes for FTM commands and events */ + +/** + * enum qca_wlan_vendor_attr_loc_capa - Indoor location capabilities + * + * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAGS: Various flags. See + * enum qca_wlan_vendor_attr_loc_capa_flags. + * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_SESSIONS: Maximum number + * of measurement sessions that can run concurrently. + * Default is one session (no session concurrency). + * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_PEERS: The total number of unique + * peers that are supported in running sessions. For example, + * if the value is 8 and maximum number of sessions is 2, you can + * have one session with 8 unique peers, or 2 sessions with 4 unique + * peers each, and so on. + * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_BURSTS_EXP: Maximum number + * of bursts per peer, as an exponent (2^value). Default is 0, + * meaning no multi-burst support. + * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_MEAS_PER_BURST: Maximum number + * of measurement exchanges allowed in a single burst. + * @QCA_WLAN_VENDOR_ATTR_AOA_CAPA_SUPPORTED_TYPES: Supported AOA measurement + * types. A bit mask (unsigned 32 bit value), each bit corresponds + * to an AOA type as defined by enum qca_vendor_attr_aoa_type. + */ +enum qca_wlan_vendor_attr_loc_capa { + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_INVALID, + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAGS, + QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_SESSIONS, + QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_PEERS, + QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_BURSTS_EXP, + QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_MEAS_PER_BURST, + QCA_WLAN_VENDOR_ATTR_AOA_CAPA_SUPPORTED_TYPES, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_MAX = + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_loc_capa_flags: Indoor location capability flags + * + * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_RESPONDER: Set if driver + * can be configured as an FTM responder (for example, an AP that + * services FTM requests). QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER + * will be supported if set. + * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR: Set if driver + * can run FTM sessions. QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION + * will be supported if set. +* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP: Set if FTM responder + * supports immediate (ASAP) response. + * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA: Set if driver supports standalone + * AOA measurement using QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS. + * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA_IN_FTM: Set if driver supports + * requesting AOA measurements as part of an FTM session. + */ +enum qca_wlan_vendor_attr_loc_capa_flags { + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_RESPONDER = 1 << 0, + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR = 1 << 1, + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP = 1 << 2, + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA = 1 << 3, + QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA_IN_FTM = 1 << 4, +}; + +/** + * enum qca_wlan_vendor_attr_ftm_peer_info: Information about + * a single peer in a measurement session. + * + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR: The MAC address of the peer. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS: Various flags related + * to measurement. See enum qca_wlan_vendor_attr_ftm_peer_meas_flags. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS: Nested attribute of + * FTM measurement parameters, as specified by IEEE P802.11-REVmc/D7.0 + * 9.4.2.167. See enum qca_wlan_vendor_attr_ftm_meas_param for + * list of supported attributes. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID: Initial token ID for + * secure measurement. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD: Request AOA + * measurement every <value> bursts. If 0 or not specified, + * AOA measurements will be disabled for this peer. + */ +enum qca_wlan_vendor_attr_ftm_peer_info { + QCA_WLAN_VENDOR_ATTR_FTM_PEER_INVALID, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX = + QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_ftm_peer_meas_flags: Measurement request flags, + * per-peer + * + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_ASAP: If set, request + * immediate (ASAP) response from peer. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI: If set, request + * LCI report from peer. The LCI report includes the absolute + * location of the peer in "official" coordinates (similar to GPS). + * See IEEE P802.11-REVmc/D7.0, 11.24.6.7 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR: If set, request + * Location civic report from peer. The LCR includes the location + * of the peer in free-form format. See IEEE P802.11-REVmc/D7.0, + * 11.24.6.7 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE: If set, + * request a secure measurement. + * QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID must also be provided. + */ +enum qca_wlan_vendor_attr_ftm_peer_meas_flags { + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_ASAP = 1 << 0, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI = 1 << 1, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR = 1 << 2, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE = 1 << 3, +}; + +/** + * enum qca_wlan_vendor_attr_ftm_meas_param: Measurement parameters + * + * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST: Number of measurements + * to perform in a single burst. + * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP: Number of bursts to + * perform, specified as an exponent (2^value). + * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION: Duration of burst + * instance, as specified in IEEE P802.11-REVmc/D7.0, 9.4.2.167. + * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD: Time between bursts, + * as specified in IEEE P802.11-REVmc/D7.0, 9.4.2.167. Must + * be larger than QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION. + */ +enum qca_wlan_vendor_attr_ftm_meas_param { + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_INVALID, + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST, + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP, + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION, + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MAX = + QCA_WLAN_VENDOR_ATTR_FTM_PARAM_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_ftm_peer_result: Per-peer results + * + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAC_ADDR: MAC address of the reported + * peer. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS: Status of measurement + * request for this peer. + * See enum qca_wlan_vendor_attr_ftm_peer_result_status. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAGS: Various flags related + * to measurement results for this peer. + * See enum qca_wlan_vendor_attr_ftm_peer_result_flags. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS: Specified when + * request failed and peer requested not to send an additional request + * for this number of seconds. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCI: LCI report when received + * from peer. In the format specified by IEEE P802.11-REVmc/D7.0, + * 9.4.2.22.10. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCR: Location civic report when + * received from peer. In the format specified by IEEE P802.11-REVmc/D7.0, + * 9.4.2.22.13. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS: Reported when peer + * overridden some measurement request parameters. See + * enum qca_wlan_vendor_attr_ftm_meas_param. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AOA_MEAS: AOA measurement + * for this peer. Same contents as @QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS: Array of measurement + * results. Each entry is a nested attribute defined + * by enum qca_wlan_vendor_attr_ftm_meas. + */ +enum qca_wlan_vendor_attr_ftm_peer_result { + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_INVALID, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAC_ADDR, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAGS, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCI, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCR, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AOA_MEAS, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAX = + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_ftm_peer_result_status + * + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK: Request sent ok and results + * will be provided. Peer may have overridden some measurement parameters, + * in which case overridden parameters will be report by + * QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAM attribute. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INCAPABLE: Peer is incapable + * of performing the measurement request. No more results will be sent + * for this peer in this session. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_FAILED: Peer reported request + * failed, and requested not to send an additional request for number + * of seconds specified by QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS + * attribute. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID: Request validation + * failed. Request was not sent over the air. + */ +enum qca_wlan_vendor_attr_ftm_peer_result_status { + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INCAPABLE, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_FAILED, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID, +}; + +/** + * enum qca_wlan_vendor_attr_ftm_peer_result_flags: Various flags + * for measurement result, per-peer + * + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAG_DONE: If set, + * measurement completed for this peer. No more results will be reported + * for this peer in this session. + */ +enum qca_wlan_vendor_attr_ftm_peer_result_flags { + QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAG_DONE = 1 << 0, +}; + +/** + * enum qca_vendor_attr_loc_session_status: Session completion status code + * + * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_OK: Session completed + * successfully. + * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED: Session aborted + * by request. + * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_INVALID: Session request + * was invalid and was not started. + * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED: Session had an error + * and did not complete normally (for example out of resources). + */ +enum qca_vendor_attr_loc_session_status { + QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_OK, + QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED, + QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_INVALID, + QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED, +}; + +/** + * enum qca_wlan_vendor_attr_ftm_meas: Single measurement data + * + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T1: Time of departure (TOD) of FTM packet as + * recorded by responder, in picoseconds. + * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T2: Time of arrival (TOA) of FTM packet at + * initiator, in picoseconds. + * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T3: TOD of ACK packet as recorded by + * initiator, in picoseconds. + * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T4: TOA of ACK packet at + * responder, in picoseconds. + * See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_RSSI: RSSI (signal level) as recorded + * during this measurement exchange. Optional and will be provided if + * the hardware can measure it. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOD_ERR: TOD error reported by + * responder. Not always provided. + * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOA_ERR: TOA error reported by + * responder. Not always provided. + * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOD_ERR: TOD error measured by + * initiator. Not always provided. + * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOA_ERR: TOA error measured by + * initiator. Not always provided. + * See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information. + * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD: Dummy attribute for padding. + */ +enum qca_wlan_vendor_attr_ftm_meas { + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INVALID, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T1, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T2, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T3, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T4, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_RSSI, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOD_ERR, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOA_ERR, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOD_ERR, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOA_ERR, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_MAX = + QCA_WLAN_VENDOR_ATTR_FTM_MEAS_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_aoa_type - AOA measurement type + * + * @QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE: Phase of the strongest + * CIR (channel impulse response) path for each antenna. + * @QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP: Phase and amplitude + * of the strongest CIR path for each antenna. + */ +enum qca_wlan_vendor_attr_aoa_type { + QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE, + QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP, + QCA_WLAN_VENDOR_ATTR_AOA_TYPE_MAX +}; + +/** + * enum qca_wlan_vendor_attr_encryption_test - Attributes to + * validate encryption engine + * + * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION: Flag attribute. + * This will be included if the request is for decryption; if not included, + * the request is treated as a request for encryption by default. + * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER: Unsigned 32-bit value + * indicating the key cipher suite. Takes same values as + * NL80211_ATTR_KEY_CIPHER. + * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID: Unsigned 8-bit value + * Key Id to be used for encryption + * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK: Array of 8-bit values. + * Key (TK) to be used for encryption/decryption + * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN: Array of 8-bit values. + * Packet number to be specified for encryption/decryption + * 6 bytes for TKIP/CCMP/GCMP. + * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA: Array of 8-bit values + * representing the 802.11 packet (header + payload + FCS) that + * needs to be encrypted/decrypted. + * Encrypted/decrypted response from the driver will also be sent + * to userspace with the same attribute. + */ +enum qca_wlan_vendor_attr_encryption_test { + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION, + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER, + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID, + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK, + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN, + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_MAX = + QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST - 1 }; #endif /* QCA_VENDOR_H */ diff --git a/src/common/sae.c b/src/common/sae.c index 503fa1d7b9a96..9f70f036ba763 100644 --- a/src/common/sae.c +++ b/src/common/sae.c @@ -1,6 +1,6 @@ /* * Simultaneous authentication of equals - * Copyright (c) 2012-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2012-2016, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -275,8 +275,9 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ bits = crypto_ec_prime_len_bits(sae->tmp->ec); - sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", - prime, sae->tmp->prime_len, pwd_value, bits); + if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", + 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); wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", @@ -318,11 +319,10 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ - sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", - sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, - bits); - if (bits % 8) - buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); + if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", + sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, + bits) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, sae->tmp->prime_len); @@ -811,11 +811,13 @@ static int sae_derive_keys(struct sae_data *sae, const u8 *k) crypto_bignum_mod(tmp, sae->tmp->order, tmp); crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len); wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); - sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", - val, sae->tmp->prime_len, keys, sizeof(keys)); + if (sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", + val, sae->tmp->prime_len, keys, sizeof(keys)) < 0) + goto fail; os_memset(keyseed, 0, sizeof(keyseed)); os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN); + os_memcpy(sae->pmkid, val, SAE_PMKID_LEN); os_memset(keys, 0, sizeof(keys)); wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); @@ -923,7 +925,7 @@ static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, const u8 *end, const u8 **token, size_t *token_len) { - if (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end) { + if ((sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end - *pos) { size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len); wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); @@ -946,7 +948,7 @@ static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, { struct crypto_bignum *peer_scalar; - if (*pos + sae->tmp->prime_len > end) { + if (sae->tmp->prime_len > end - *pos) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } @@ -994,7 +996,7 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, { u8 prime[SAE_MAX_ECC_PRIME_LEN]; - if (pos + 2 * sae->tmp->prime_len > end) { + if (2 * sae->tmp->prime_len > end - pos) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for " "commit-element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -1040,7 +1042,7 @@ static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, struct crypto_bignum *res, *one; const u8 one_bin[1] = { 0x01 }; - if (pos + sae->tmp->prime_len > end) { + if (sae->tmp->prime_len > end - pos) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for " "commit-element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -1098,7 +1100,7 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, u16 res; /* Check Finite Cyclic Group */ - if (pos + 2 > end) + if (end - pos < 2) return WLAN_STATUS_UNSPECIFIED_FAILURE; res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos)); if (res != WLAN_STATUS_SUCCESS) diff --git a/src/common/sae.h b/src/common/sae.h index c07026cd497cc..a4270bc22d14a 100644 --- a/src/common/sae.h +++ b/src/common/sae.h @@ -45,6 +45,7 @@ struct sae_data { enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state; u16 send_confirm; u8 pmk[SAE_PMK_LEN]; + u8 pmkid[SAE_PMKID_LEN]; struct crypto_bignum *peer_commit_scalar; int group; int sync; diff --git a/src/common/version.h b/src/common/version.h index a5cc5b7b5bccb..75e5c6e006cce 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -5,6 +5,10 @@ #define VERSION_STR_POSTFIX "" #endif /* VERSION_STR_POSTFIX */ -#define VERSION_STR "2.5" VERSION_STR_POSTFIX +#ifndef GIT_VERSION_STR_POSTFIX +#define GIT_VERSION_STR_POSTFIX "" +#endif /* GIT_VERSION_STR_POSTFIX */ + +#define VERSION_STR "2.6" 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 e9d4248d72d45..299b8bbee031a 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -292,38 +292,47 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, pos = ie + sizeof(struct rsn_ftie); end = ie + ie_len; - while (pos + 2 <= end && pos + 2 + pos[1] <= end) { - switch (pos[0]) { + while (end - pos >= 2) { + u8 id, len; + + id = *pos++; + len = *pos++; + if (len > end - pos) + break; + + switch (id) { case FTIE_SUBELEM_R1KH_ID: - if (pos[1] != FT_R1KH_ID_LEN) { - wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID " - "length in FTIE: %d", pos[1]); + if (len != FT_R1KH_ID_LEN) { + wpa_printf(MSG_DEBUG, + "FT: Invalid R1KH-ID length in FTIE: %d", + len); return -1; } - parse->r1kh_id = pos + 2; + parse->r1kh_id = pos; break; case FTIE_SUBELEM_GTK: - parse->gtk = pos + 2; - parse->gtk_len = pos[1]; + parse->gtk = pos; + parse->gtk_len = len; break; case FTIE_SUBELEM_R0KH_ID: - if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) { - wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID " - "length in FTIE: %d", pos[1]); + if (len < 1 || len > FT_R0KH_ID_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "FT: Invalid R0KH-ID length in FTIE: %d", + len); return -1; } - parse->r0kh_id = pos + 2; - parse->r0kh_id_len = pos[1]; + parse->r0kh_id = pos; + parse->r0kh_id_len = len; break; #ifdef CONFIG_IEEE80211W case FTIE_SUBELEM_IGTK: - parse->igtk = pos + 2; - parse->igtk_len = pos[1]; + parse->igtk = pos; + parse->igtk_len = len; break; #endif /* CONFIG_IEEE80211W */ } - pos += 2 + pos[1]; + pos += len; } return 0; @@ -345,11 +354,18 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, pos = ies; end = ies + ies_len; - while (pos + 2 <= end && pos + 2 + pos[1] <= end) { - switch (pos[0]) { + while (end - pos >= 2) { + u8 id, len; + + id = *pos++; + len = *pos++; + if (len > end - pos) + break; + + switch (id) { case WLAN_EID_RSN: - parse->rsn = pos + 2; - parse->rsn_len = pos[1]; + parse->rsn = pos; + parse->rsn_len = len; ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, parse->rsn_len + 2, &data); @@ -362,32 +378,32 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, parse->rsn_pmkid = data.pmkid; break; case WLAN_EID_MOBILITY_DOMAIN: - if (pos[1] < sizeof(struct rsn_mdie)) + if (len < sizeof(struct rsn_mdie)) return -1; - parse->mdie = pos + 2; - parse->mdie_len = pos[1]; + parse->mdie = pos; + parse->mdie_len = len; break; case WLAN_EID_FAST_BSS_TRANSITION: - if (pos[1] < sizeof(*ftie)) + if (len < sizeof(*ftie)) return -1; - ftie = (const struct rsn_ftie *) (pos + 2); + ftie = (const struct rsn_ftie *) pos; prot_ie_count = ftie->mic_control[1]; - if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) + if (wpa_ft_parse_ftie(pos, len, parse) < 0) return -1; break; case WLAN_EID_TIMEOUT_INTERVAL: - if (pos[1] != 5) + if (len != 5) break; - parse->tie = pos + 2; - parse->tie_len = pos[1]; + parse->tie = pos; + parse->tie_len = len; break; case WLAN_EID_RIC_DATA: if (parse->ric == NULL) - parse->ric = pos; + parse->ric = pos - 2; break; } - pos += 2 + pos[1]; + pos += len; } if (prot_ie_count == 0) @@ -416,13 +432,15 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, } /* Determine the end of the RIC IE(s) */ - pos = parse->ric; - while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end && - prot_ie_count) { - prot_ie_count--; - pos += 2 + pos[1]; + if (parse->ric) { + pos = parse->ric; + while (end - pos >= 2 && 2 + pos[1] <= end - pos && + prot_ie_count) { + prot_ie_count--; + pos += 2 + pos[1]; + } + parse->ric_len = pos - parse->ric; } - parse->ric_len = pos - parse->ric; if (prot_ie_count) { wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from " "frame", (int) prot_ie_count); @@ -582,8 +600,10 @@ 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); if (!wpa_cipher_valid_group(data->group_cipher)) { - wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x", - __func__, data->group_cipher); + wpa_printf(MSG_DEBUG, + "%s: invalid group cipher 0x%x (%08x)", + __func__, data->group_cipher, + WPA_GET_BE32(pos)); return -1; } pos += RSN_SELECTOR_LEN; @@ -671,9 +691,10 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, if (left >= 4) { data->mgmt_group_cipher = rsn_selector_to_bitfield(pos); if (!wpa_cipher_valid_mgmt_group(data->mgmt_group_cipher)) { - wpa_printf(MSG_DEBUG, "%s: Unsupported management " - "group cipher 0x%x", __func__, - data->mgmt_group_cipher); + wpa_printf(MSG_DEBUG, + "%s: Unsupported management group cipher 0x%x (%08x)", + __func__, data->mgmt_group_cipher, + WPA_GET_BE32(pos)); return -10; } pos += RSN_SELECTOR_LEN; @@ -1163,6 +1184,8 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) "WPA2-PSK" : "WPA-PSK"; case WPA_KEY_MGMT_NONE: return "NONE"; + case WPA_KEY_MGMT_WPA_NONE: + return "WPA-NONE"; case WPA_KEY_MGMT_IEEE8021X_NO_WPA: return "IEEE 802.1X (no WPA)"; #ifdef CONFIG_IEEE80211R @@ -1261,13 +1284,13 @@ int wpa_compare_rsn_ie(int ft_initial_assoc, #ifdef CONFIG_IEEE80211R -int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) +int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid) { u8 *start, *end, *rpos, *rend; int added = 0; start = ies; - end = ies + ies_len; + end = ies + *ies_len; while (start < end) { if (*start == WLAN_EID_RSN) @@ -1320,11 +1343,29 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) added += 2 + PMKID_LEN; start[1] += 2 + PMKID_LEN; } else { - /* PMKID-Count was included; use it */ - if (WPA_GET_LE16(rpos) != 0) { - wpa_printf(MSG_ERROR, "FT: Unexpected PMKID " - "in RSN IE in EAPOL-Key data"); + u16 num_pmkid; + + if (rend - rpos < 2) return -1; + num_pmkid = WPA_GET_LE16(rpos); + /* PMKID-Count was included; use it */ + if (num_pmkid != 0) { + u8 *after; + + if (num_pmkid * PMKID_LEN > rend - rpos - 2) + return -1; + /* + * PMKID may have been included in RSN IE in + * (Re)Association Request frame, so remove the old + * PMKID(s) first before adding the new one. + */ + wpa_printf(MSG_DEBUG, + "FT: Remove %u old PMKID(s) from RSN IE", + num_pmkid); + after = rpos + 2 + num_pmkid * PMKID_LEN; + os_memmove(rpos + 2, after, rend - after); + start[1] -= num_pmkid * PMKID_LEN; + added -= num_pmkid * PMKID_LEN; } WPA_PUT_LE16(rpos, 1); rpos += 2; @@ -1337,7 +1378,9 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) wpa_hexdump(MSG_DEBUG, "FT: RSN IE after modification " "(PMKID inserted)", start, 2 + start[1]); - return added; + *ies_len += added; + + return 0; } #endif /* CONFIG_IEEE80211R */ diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index c08f6514ab573..af1d0f0c6efbc 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -12,6 +12,8 @@ /* IEEE 802.11i */ #define PMKID_LEN 16 #define PMK_LEN 32 +#define PMK_LEN_SUITE_B_192 48 +#define PMK_LEN_MAX 48 #define WPA_REPLAY_COUNTER_LEN 8 #define WPA_NONCE_LEN 32 #define WPA_KEY_RSC_LEN 8 @@ -407,7 +409,7 @@ u32 wpa_akm_to_suite(int akm); int wpa_compare_rsn_ie(int ft_initial_assoc, const u8 *ie1, size_t ie1len, const u8 *ie2, size_t ie2len); -int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid); +int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid); struct wpa_ft_ies { const u8 *mdie; diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c index 5733aa605d18c..623c2a768e43d 100644 --- a/src/common/wpa_ctrl.c +++ b/src/common/wpa_ctrl.c @@ -532,6 +532,8 @@ retry_send: FD_ZERO(&rfds); FD_SET(ctrl->s, &rfds); res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + if (res < 0 && errno == EINTR) + continue; if (res < 0) return res; if (FD_ISSET(ctrl->s, &rfds)) { diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 3de46823588b8..4dcba81dc1a44 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -76,6 +76,21 @@ extern "C" { #define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE " /** Regulatory domain channel */ #define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE " +/** Channel switch (followed by freq=<MHz> and other channel parameters) */ +#define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH " + +/** IP subnet status change notification + * + * When using an offloaded roaming mechanism where driver/firmware takes care + * of roaming and IP subnet validation checks post-roaming, this event can + * indicate whether IP subnet has changed. + * + * The event has a status=<0/1/2> parameter where + * 0 = unknown + * 1 = IP subnet unchanged (can continue to use the old IP address) + * 2 = IP subnet changed (need to get a new IP address) + */ +#define WPA_EVENT_SUBNET_STATUS_UPDATE "CTRL-EVENT-SUBNET-STATUS-UPDATE " /** RSN IBSS 4-way handshakes completed with specified peer */ #define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED " @@ -174,6 +189,7 @@ extern "C" { #define P2P_EVENT_SERV_ASP_RESP "P2P-SERV-ASP-RESP " #define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED " #define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT " +#define P2P_EVENT_INVITATION_ACCEPTED "P2P-INVITATION-ACCEPTED " #define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED " #define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id=" #define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE " @@ -212,6 +228,11 @@ extern "C" { /* parameters: <addr> <result> */ #define ANQP_QUERY_DONE "ANQP-QUERY-DONE " +#define RX_ANQP "RX-ANQP " +#define RX_HS20_ANQP "RX-HS20-ANQP " +#define RX_HS20_ANQP_ICON "RX-HS20-ANQP-ICON " +#define RX_HS20_ICON "RX-HS20-ICON " + #define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION " #define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE " @@ -232,6 +253,7 @@ extern "C" { #define AP_STA_CONNECTED "AP-STA-CONNECTED " #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED " #define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH " +#define AP_STA_POLL_OK "AP-STA-POLL-OK " #define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA " #define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA " @@ -254,9 +276,18 @@ extern "C" { #define AP_CSA_FINISHED "AP-CSA-FINISHED " +#define P2P_EVENT_LISTEN_OFFLOAD_STOP "P2P-LISTEN-OFFLOAD-STOPPED " +#define P2P_LISTEN_OFFLOAD_STOP_REASON "P2P-LISTEN-OFFLOAD-STOP-REASON " + /* BSS Transition Management Response frame received */ #define BSS_TM_RESP "BSS-TM-RESP " +/* MBO IE with cellular data connection preference received */ +#define MBO_CELL_PREFERENCE "MBO-CELL-PREFERENCE " + +/* BSS Transition Management Request received with MBO transition reason */ +#define MBO_TRANSITION_REASON "MBO-TRANSITION-REASON " + /* BSS command information masks */ #define WPA_BSS_MASK_ALL 0xFFFDFFFF @@ -300,6 +331,7 @@ enum wpa_vendor_elem_frame { VENDOR_ELEM_P2P_ASSOC_REQ = 11, VENDOR_ELEM_P2P_ASSOC_RESP = 12, VENDOR_ELEM_ASSOC_REQ = 13, + VENDOR_ELEM_PROBE_REQ = 14, NUM_VENDOR_ELEM_FRAMES }; diff --git a/src/common/wpa_helpers.c b/src/common/wpa_helpers.c index 28913b9139aaa..f1594213f97f5 100644 --- a/src/common/wpa_helpers.c +++ b/src/common/wpa_helpers.c @@ -172,7 +172,8 @@ int get_wpa_status(const char *ifname, const char *field, char *obuf, if (ctrl == NULL) return -1; len = sizeof(buf); - if (wpa_ctrl_request(ctrl, "STATUS", 6, buf, &len, NULL) < 0) { + if (wpa_ctrl_request(ctrl, "STATUS-NO_EVENTS", 16, buf, &len, + NULL) < 0) { wpa_ctrl_close(ctrl); return -1; } diff --git a/src/crypto/Makefile b/src/crypto/Makefile index 3e90350c103ee..d181e723134eb 100644 --- a/src/crypto/Makefile +++ b/src/crypto/Makefile @@ -47,7 +47,9 @@ LIB_OBJS= \ sha256.o \ sha256-prf.o \ sha256-tlsprf.o \ - sha256-internal.o + sha256-internal.o \ + sha384-internal.o \ + sha512-internal.o LIB_OBJS += crypto_internal.o LIB_OBJS += crypto_internal-cipher.o diff --git a/src/crypto/aes-cbc.c b/src/crypto/aes-cbc.c index 2833cfcc840d9..0835f2cfb77ec 100644 --- a/src/crypto/aes-cbc.c +++ b/src/crypto/aes-cbc.c @@ -28,6 +28,9 @@ int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) u8 *pos = data; int i, j, blocks; + if (TEST_FAIL()) + return -1; + ctx = aes_encrypt_init(key, 16); if (ctx == NULL) return -1; @@ -61,6 +64,9 @@ int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) u8 *pos = data; int i, j, blocks; + if (TEST_FAIL()) + return -1; + ctx = aes_decrypt_init(key, 16); if (ctx == NULL) return -1; diff --git a/src/crypto/aes-omac1.c b/src/crypto/aes-omac1.c index 375db5735be33..8642516340c69 100644 --- a/src/crypto/aes-omac1.c +++ b/src/crypto/aes-omac1.c @@ -48,6 +48,9 @@ int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *pos, *end; size_t i, e, left, total_len; + if (TEST_FAIL()) + return -1; + ctx = aes_encrypt_init(key, key_len); if (ctx == NULL) return -1; diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 534c4bd786543..bdc3ba6f37e0d 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -80,6 +80,28 @@ int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); /** + * sha384_vector - SHA384 hash for data vector + * @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 + * Returns: 0 on success, -1 on failure + */ +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +/** + * sha512_vector - SHA512 hash for data vector + * @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 + * Returns: 0 on success, -1 on failure + */ +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +/** * des_encrypt - Encrypt one block with DES * @clear: 8 octets (in) * @key: 7 octets (in) (no parity bits included) @@ -135,7 +157,8 @@ void aes_decrypt_deinit(void *ctx); enum crypto_hash_alg { CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1, CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1, - CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256 + CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256, + CRYPTO_HASH_ALG_SHA384, CRYPTO_HASH_ALG_SHA512 }; struct crypto_hash; diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c index f3602dac346ea..d391f48ab5b18 100644 --- a/src/crypto/crypto_internal.c +++ b/src/crypto/crypto_internal.c @@ -11,6 +11,8 @@ #include "common.h" #include "crypto.h" #include "sha256_i.h" +#include "sha384_i.h" +#include "sha512_i.h" #include "sha1_i.h" #include "md5_i.h" @@ -22,6 +24,12 @@ struct crypto_hash { #ifdef CONFIG_SHA256 struct sha256_state sha256; #endif /* CONFIG_SHA256 */ +#ifdef CONFIG_INTERNAL_SHA384 + struct sha384_state sha384; +#endif /* CONFIG_INTERNAL_SHA384 */ +#ifdef CONFIG_INTERNAL_SHA512 + struct sha512_state sha512; +#endif /* CONFIG_INTERNAL_SHA512 */ } u; u8 key[64]; size_t key_len; @@ -54,6 +62,16 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, sha256_init(&ctx->u.sha256); break; #endif /* CONFIG_SHA256 */ +#ifdef CONFIG_INTERNAL_SHA384 + case CRYPTO_HASH_ALG_SHA384: + sha384_init(&ctx->u.sha384); + break; +#endif /* CONFIG_INTERNAL_SHA384 */ +#ifdef CONFIG_INTERNAL_SHA512 + case CRYPTO_HASH_ALG_SHA512: + sha512_init(&ctx->u.sha512); + break; +#endif /* CONFIG_INTERNAL_SHA512 */ case CRYPTO_HASH_ALG_HMAC_MD5: if (key_len > sizeof(k_pad)) { MD5Init(&ctx->u.md5); @@ -142,6 +160,16 @@ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) sha256_process(&ctx->u.sha256, data, len); break; #endif /* CONFIG_SHA256 */ +#ifdef CONFIG_INTERNAL_SHA384 + case CRYPTO_HASH_ALG_SHA384: + sha384_process(&ctx->u.sha384, data, len); + break; +#endif /* CONFIG_INTERNAL_SHA384 */ +#ifdef CONFIG_INTERNAL_SHA512 + case CRYPTO_HASH_ALG_SHA512: + sha512_process(&ctx->u.sha512, data, len); + break; +#endif /* CONFIG_INTERNAL_SHA512 */ default: break; } @@ -191,6 +219,28 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) sha256_done(&ctx->u.sha256, mac); break; #endif /* CONFIG_SHA256 */ +#ifdef CONFIG_INTERNAL_SHA384 + case CRYPTO_HASH_ALG_SHA384: + if (*len < 48) { + *len = 48; + os_free(ctx); + return -1; + } + *len = 48; + sha384_done(&ctx->u.sha384, mac); + break; +#endif /* CONFIG_INTERNAL_SHA384 */ +#ifdef CONFIG_INTERNAL_SHA512 + case CRYPTO_HASH_ALG_SHA512: + if (*len < 64) { + *len = 64; + os_free(ctx); + return -1; + } + *len = 64; + sha512_done(&ctx->u.sha512, mac); + break; +#endif /* CONFIG_INTERNAL_SHA512 */ case CRYPTO_HASH_ALG_HMAC_MD5: if (*len < 16) { *len = 16; diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c index 581005df3e39e..ffd23942e32d7 100644 --- a/src/crypto/crypto_module_tests.c +++ b/src/crypto/crypto_module_tests.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/module_tests.h" #include "crypto/aes_siv.h" #include "crypto/aes_wrap.h" #include "crypto/aes.h" @@ -1266,7 +1267,7 @@ static int test_sha1(void) } -const struct { +static const struct { char *data; u8 hash[32]; } tests[] = { @@ -1290,7 +1291,7 @@ const struct { } }; -const struct hmac_test { +static const struct hmac_test { u8 key[80]; size_t key_len; u8 data[128]; @@ -1503,6 +1504,7 @@ static int test_sha256(void) const u8 *addr[2]; size_t len[2]; int errors = 0; + u8 *key; for (i = 0; i < ARRAY_SIZE(tests); i++) { wpa_printf(MSG_INFO, "SHA256 test case %d:", i + 1); @@ -1573,12 +1575,66 @@ static int test_sha256(void) hash, sizeof(hash)); /* TODO: add proper test case for this */ + key = os_malloc(8161); + if (key) { +#ifdef CONFIG_HMAC_SHA256_KDF + int res; + + res = hmac_sha256_kdf((u8 *) "secret", 6, "label", + (u8 *) "seed", 4, key, 8160); + if (res) { + wpa_printf(MSG_INFO, + "Unexpected hmac_sha256_kdf(outlen=8160) failure"); + errors++; + } + + res = hmac_sha256_kdf((u8 *) "secret", 6, "label", + (u8 *) "seed", 4, key, 8161); + if (res == 0) { + wpa_printf(MSG_INFO, + "Unexpected hmac_sha256_kdf(outlen=8161) success"); + errors++; + } +#endif /* CONFIG_HMAC_SHA256_KDF */ + + os_free(key); + } + if (!errors) wpa_printf(MSG_INFO, "SHA256 test cases passed"); return errors; } +static int test_fips186_2_prf(void) +{ + /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */ + u8 xkey[] = { + 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b, + 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f, + 0xeb, 0x5a, 0x38, 0xb6 + }; + u8 w[] = { + 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f, + 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49, + 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba, + 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78, + 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16 + }; + u8 buf[40]; + + wpa_printf(MSG_INFO, + "Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)"); + if (fips186_2_prf(xkey, sizeof(xkey), buf, sizeof(buf)) < 0 || + os_memcmp(w, buf, sizeof(w)) != 0) { + wpa_printf(MSG_INFO, "fips186_2_prf failed"); + return 1; + } + + return 0; +} + + static int test_ms_funcs(void) { #ifndef CONFIG_FIPS @@ -1695,6 +1751,7 @@ int crypto_module_tests(void) test_md5() || test_sha1() || test_sha256() || + test_fips186_2_prf() || test_ms_funcs()) ret = -1; diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index 6cff75c64ae5a..19e0e2be87be3 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -29,11 +29,61 @@ #include "sha1.h" #include "sha256.h" #include "sha384.h" +#include "md5.h" +#include "aes_wrap.h" #include "crypto.h" +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +/* Compatibility wrappers for older versions. */ + +static HMAC_CTX * HMAC_CTX_new(void) +{ + HMAC_CTX *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx) + HMAC_CTX_init(ctx); + return ctx; +} + + +static void HMAC_CTX_free(HMAC_CTX *ctx) +{ + if (!ctx) + return; + HMAC_CTX_cleanup(ctx); + bin_clear_free(ctx, sizeof(*ctx)); +} + + +static EVP_MD_CTX * EVP_MD_CTX_new(void) +{ + EVP_MD_CTX *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx) + EVP_MD_CTX_init(ctx); + return ctx; +} + + +static void EVP_MD_CTX_free(EVP_MD_CTX *ctx) +{ + if (!ctx) + return; + EVP_MD_CTX_cleanup(ctx); + bin_clear_free(ctx, sizeof(*ctx)); +} + +#endif /* OpenSSL version < 1.1.0 */ + static BIGNUM * get_group5_prime(void) { -#ifdef OPENSSL_IS_BORINGSSL +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) + return BN_get_rfc3526_prime_1536(NULL); +#elif !defined(OPENSSL_IS_BORINGSSL) + return get_rfc3526_prime_1536(NULL); +#else static const unsigned char RFC3526_PRIME_1536[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, @@ -53,9 +103,7 @@ static BIGNUM * get_group5_prime(void) 0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, }; return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL); -#else /* OPENSSL_IS_BORINGSSL */ - return get_rfc3526_prime_1536(NULL); -#endif /* OPENSSL_IS_BORINGSSL */ +#endif } #ifdef OPENSSL_NO_SHA256 @@ -65,29 +113,38 @@ static BIGNUM * get_group5_prime(void) static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - EVP_MD_CTX ctx; + EVP_MD_CTX *ctx; size_t i; unsigned int mac_len; - EVP_MD_CTX_init(&ctx); - if (!EVP_DigestInit_ex(&ctx, type, NULL)) { + if (TEST_FAIL()) + return -1; + + ctx = EVP_MD_CTX_new(); + if (!ctx) + return -1; + if (!EVP_DigestInit_ex(ctx, type, NULL)) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_MD_CTX_free(ctx); return -1; } for (i = 0; i < num_elem; i++) { - if (!EVP_DigestUpdate(&ctx, addr[i], len[i])) { + if (!EVP_DigestUpdate(ctx, addr[i], len[i])) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestUpdate " "failed: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_MD_CTX_free(ctx); return -1; } } - if (!EVP_DigestFinal(&ctx, mac, &mac_len)) { + if (!EVP_DigestFinal(ctx, mac, &mac_len)) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s", ERR_error_string(ERR_get_error(), NULL)); + EVP_MD_CTX_free(ctx); return -1; } + EVP_MD_CTX_free(ctx); return 0; } @@ -129,32 +186,34 @@ int rc4_skip(const u8 *key, size_t keylen, size_t skip, #ifdef OPENSSL_NO_RC4 return -1; #else /* OPENSSL_NO_RC4 */ - EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX *ctx; int outl; int res = -1; unsigned char skip_buf[16]; - EVP_CIPHER_CTX_init(&ctx); - if (!EVP_CIPHER_CTX_set_padding(&ctx, 0) || - !EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, NULL, NULL, 1) || - !EVP_CIPHER_CTX_set_key_length(&ctx, keylen) || - !EVP_CipherInit_ex(&ctx, NULL, NULL, key, NULL, 1)) + ctx = EVP_CIPHER_CTX_new(); + if (!ctx || + !EVP_CIPHER_CTX_set_padding(ctx, 0) || + !EVP_CipherInit_ex(ctx, EVP_rc4(), NULL, NULL, NULL, 1) || + !EVP_CIPHER_CTX_set_key_length(ctx, keylen) || + !EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, 1)) goto out; while (skip >= sizeof(skip_buf)) { size_t len = skip; if (len > sizeof(skip_buf)) len = sizeof(skip_buf); - if (!EVP_CipherUpdate(&ctx, skip_buf, &outl, skip_buf, len)) + if (!EVP_CipherUpdate(ctx, skip_buf, &outl, skip_buf, len)) goto out; skip -= len; } - if (EVP_CipherUpdate(&ctx, data, &outl, data, data_len)) + if (EVP_CipherUpdate(ctx, data, &outl, data, data_len)) res = 0; out: - EVP_CIPHER_CTX_cleanup(&ctx); + if (ctx) + EVP_CIPHER_CTX_free(ctx); return res; #endif /* OPENSSL_NO_RC4 */ } @@ -206,14 +265,16 @@ void * aes_encrypt_init(const u8 *key, size_t len) EVP_CIPHER_CTX *ctx; const EVP_CIPHER *type; + if (TEST_FAIL()) + return NULL; + type = aes_get_evp_cipher(len); if (type == NULL) return NULL; - ctx = os_malloc(sizeof(*ctx)); + ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) return NULL; - EVP_CIPHER_CTX_init(ctx); if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) { os_free(ctx); return NULL; @@ -247,8 +308,7 @@ void aes_encrypt_deinit(void *ctx) wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " "in AES encrypt", len); } - EVP_CIPHER_CTX_cleanup(c); - bin_clear_free(c, sizeof(*c)); + EVP_CIPHER_CTX_free(c); } @@ -257,16 +317,18 @@ void * aes_decrypt_init(const u8 *key, size_t len) EVP_CIPHER_CTX *ctx; const EVP_CIPHER *type; + if (TEST_FAIL()) + return NULL; + type = aes_get_evp_cipher(len); if (type == NULL) return NULL; - ctx = os_malloc(sizeof(*ctx)); + ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) return NULL; - EVP_CIPHER_CTX_init(ctx); if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) { - os_free(ctx); + EVP_CIPHER_CTX_free(ctx); return NULL; } EVP_CIPHER_CTX_set_padding(ctx, 0); @@ -298,8 +360,7 @@ void aes_decrypt_deinit(void *ctx) wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " "in AES decrypt", len); } - EVP_CIPHER_CTX_cleanup(c); - bin_clear_free(c, sizeof(*c)); + EVP_CIPHER_CTX_free(c); } @@ -338,51 +399,56 @@ int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) { - EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX *ctx; int clen, len; u8 buf[16]; + int res = -1; - EVP_CIPHER_CTX_init(&ctx); - if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) + if (TEST_FAIL()) return -1; - EVP_CIPHER_CTX_set_padding(&ctx, 0); - clen = data_len; - if (EVP_EncryptUpdate(&ctx, data, &clen, data, data_len) != 1 || - clen != (int) data_len) + ctx = EVP_CIPHER_CTX_new(); + if (!ctx) return -1; - + clen = data_len; len = sizeof(buf); - if (EVP_EncryptFinal_ex(&ctx, buf, &len) != 1 || len != 0) - return -1; - EVP_CIPHER_CTX_cleanup(&ctx); + if (EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) == 1 && + EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 && + EVP_EncryptUpdate(ctx, data, &clen, data, data_len) == 1 && + clen == (int) data_len && + EVP_EncryptFinal_ex(ctx, buf, &len) == 1 && len == 0) + res = 0; + EVP_CIPHER_CTX_free(ctx); - return 0; + return res; } int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) { - EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX *ctx; int plen, len; u8 buf[16]; + int res = -1; - EVP_CIPHER_CTX_init(&ctx); - if (EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) + if (TEST_FAIL()) return -1; - EVP_CIPHER_CTX_set_padding(&ctx, 0); - plen = data_len; - if (EVP_DecryptUpdate(&ctx, data, &plen, data, data_len) != 1 || - plen != (int) data_len) + ctx = EVP_CIPHER_CTX_new(); + if (!ctx) return -1; - + plen = data_len; len = sizeof(buf); - if (EVP_DecryptFinal_ex(&ctx, buf, &len) != 1 || len != 0) - return -1; - EVP_CIPHER_CTX_cleanup(&ctx); + if (EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) == 1 && + EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 && + EVP_DecryptUpdate(ctx, data, &plen, data, data_len) == 1 && + plen == (int) data_len && + EVP_DecryptFinal_ex(ctx, buf, &len) == 1 && len == 0) + res = 0; + EVP_CIPHER_CTX_free(ctx); + + return res; - return 0; } @@ -425,8 +491,8 @@ error: struct crypto_cipher { - EVP_CIPHER_CTX enc; - EVP_CIPHER_CTX dec; + EVP_CIPHER_CTX *enc; + EVP_CIPHER_CTX *dec; }; @@ -487,23 +553,25 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, return NULL; } - EVP_CIPHER_CTX_init(&ctx->enc); - EVP_CIPHER_CTX_set_padding(&ctx->enc, 0); - if (!EVP_EncryptInit_ex(&ctx->enc, cipher, NULL, NULL, NULL) || - !EVP_CIPHER_CTX_set_key_length(&ctx->enc, key_len) || - !EVP_EncryptInit_ex(&ctx->enc, NULL, NULL, key, iv)) { - EVP_CIPHER_CTX_cleanup(&ctx->enc); + if (!(ctx->enc = EVP_CIPHER_CTX_new()) || + !EVP_CIPHER_CTX_set_padding(ctx->enc, 0) || + !EVP_EncryptInit_ex(ctx->enc, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_set_key_length(ctx->enc, key_len) || + !EVP_EncryptInit_ex(ctx->enc, NULL, NULL, key, iv)) { + if (ctx->enc) + EVP_CIPHER_CTX_free(ctx->enc); os_free(ctx); return NULL; } - EVP_CIPHER_CTX_init(&ctx->dec); - EVP_CIPHER_CTX_set_padding(&ctx->dec, 0); - if (!EVP_DecryptInit_ex(&ctx->dec, cipher, NULL, NULL, NULL) || - !EVP_CIPHER_CTX_set_key_length(&ctx->dec, key_len) || - !EVP_DecryptInit_ex(&ctx->dec, NULL, NULL, key, iv)) { - EVP_CIPHER_CTX_cleanup(&ctx->enc); - EVP_CIPHER_CTX_cleanup(&ctx->dec); + if (!(ctx->dec = EVP_CIPHER_CTX_new()) || + !EVP_CIPHER_CTX_set_padding(ctx->dec, 0) || + !EVP_DecryptInit_ex(ctx->dec, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_set_key_length(ctx->dec, key_len) || + !EVP_DecryptInit_ex(ctx->dec, NULL, NULL, key, iv)) { + EVP_CIPHER_CTX_free(ctx->enc); + if (ctx->dec) + EVP_CIPHER_CTX_free(ctx->dec); os_free(ctx); return NULL; } @@ -516,7 +584,7 @@ int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, u8 *crypt, size_t len) { int outl; - if (!EVP_EncryptUpdate(&ctx->enc, crypt, &outl, plain, len)) + if (!EVP_EncryptUpdate(ctx->enc, crypt, &outl, plain, len)) return -1; return 0; } @@ -527,7 +595,7 @@ int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, { int outl; outl = len; - if (!EVP_DecryptUpdate(&ctx->dec, plain, &outl, crypt, len)) + if (!EVP_DecryptUpdate(ctx->dec, plain, &outl, crypt, len)) return -1; return 0; } @@ -535,19 +603,21 @@ int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, void crypto_cipher_deinit(struct crypto_cipher *ctx) { - EVP_CIPHER_CTX_cleanup(&ctx->enc); - EVP_CIPHER_CTX_cleanup(&ctx->dec); + EVP_CIPHER_CTX_free(ctx->enc); + EVP_CIPHER_CTX_free(ctx->dec); os_free(ctx); } void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L DH *dh; struct wpabuf *pubkey = NULL, *privkey = NULL; size_t publen, privlen; *priv = NULL; + wpabuf_free(*publ); *publ = NULL; dh = DH_new(); @@ -586,11 +656,63 @@ err: wpabuf_clear_free(privkey); DH_free(dh); return NULL; +#else + DH *dh; + struct wpabuf *pubkey = NULL, *privkey = NULL; + size_t publen, privlen; + BIGNUM *p = NULL, *g; + const BIGNUM *priv_key = NULL, *pub_key = NULL; + + *priv = NULL; + wpabuf_free(*publ); + *publ = NULL; + + dh = DH_new(); + if (dh == NULL) + return NULL; + + g = BN_new(); + p = get_group5_prime(); + if (!g || BN_set_word(g, 2) != 1 || !p || + DH_set0_pqg(dh, p, NULL, g) != 1) + goto err; + p = NULL; + g = NULL; + + if (DH_generate_key(dh) != 1) + goto err; + + DH_get0_key(dh, &pub_key, &priv_key); + publen = BN_num_bytes(pub_key); + pubkey = wpabuf_alloc(publen); + if (!pubkey) + goto err; + privlen = BN_num_bytes(priv_key); + privkey = wpabuf_alloc(privlen); + if (!privkey) + goto err; + + BN_bn2bin(pub_key, wpabuf_put(pubkey, publen)); + BN_bn2bin(priv_key, wpabuf_put(privkey, privlen)); + + *priv = privkey; + *publ = pubkey; + return dh; + +err: + BN_free(p); + BN_free(g); + wpabuf_clear_free(pubkey); + wpabuf_clear_free(privkey); + DH_free(dh); + return NULL; +#endif } void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L DH *dh; dh = DH_new(); @@ -621,6 +743,42 @@ void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) err: DH_free(dh); return NULL; +#else + DH *dh; + BIGNUM *p = NULL, *g, *priv_key = NULL, *pub_key = NULL; + + dh = DH_new(); + if (dh == NULL) + return NULL; + + g = BN_new(); + p = get_group5_prime(); + if (!g || BN_set_word(g, 2) != 1 || !p || + DH_set0_pqg(dh, p, NULL, g) != 1) + goto err; + p = NULL; + g = NULL; + + priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL); + pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL); + if (!priv_key || !pub_key || DH_set0_key(dh, pub_key, priv_key) != 1) + goto err; + pub_key = NULL; + priv_key = NULL; + + if (DH_generate_key(dh) != 1) + goto err; + + return dh; + +err: + BN_free(p); + BN_free(g); + BN_free(pub_key); + BN_clear_free(priv_key); + DH_free(dh); + return NULL; +#endif } @@ -672,7 +830,7 @@ void dh5_free(void *ctx) struct crypto_hash { - HMAC_CTX ctx; + HMAC_CTX *ctx; }; @@ -707,16 +865,17 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return NULL; - HMAC_CTX_init(&ctx->ctx); + ctx->ctx = HMAC_CTX_new(); + if (!ctx->ctx) { + os_free(ctx); + return NULL; + } -#if OPENSSL_VERSION_NUMBER < 0x00909000 - HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL); -#else /* openssl < 0.9.9 */ - if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) { + if (HMAC_Init_ex(ctx->ctx, key, key_len, md, NULL) != 1) { + HMAC_CTX_free(ctx->ctx); bin_clear_free(ctx, sizeof(*ctx)); return NULL; } -#endif /* openssl < 0.9.9 */ return ctx; } @@ -726,7 +885,7 @@ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) { if (ctx == NULL) return; - HMAC_Update(&ctx->ctx, data, len); + HMAC_Update(ctx->ctx, data, len); } @@ -739,18 +898,14 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) return -2; if (mac == NULL || len == NULL) { + HMAC_CTX_free(ctx->ctx); bin_clear_free(ctx, sizeof(*ctx)); return 0; } mdlen = *len; -#if OPENSSL_VERSION_NUMBER < 0x00909000 - HMAC_Final(&ctx->ctx, mac, &mdlen); - res = 1; -#else /* openssl < 0.9.9 */ - res = HMAC_Final(&ctx->ctx, mac, &mdlen); -#endif /* openssl < 0.9.9 */ - HMAC_CTX_cleanup(&ctx->ctx); + res = HMAC_Final(ctx->ctx, mac, &mdlen); + HMAC_CTX_free(ctx->ctx); bin_clear_free(ctx, sizeof(*ctx)); if (res == 1) { @@ -767,28 +922,26 @@ static int openssl_hmac_vector(const EVP_MD *type, const u8 *key, const u8 *addr[], const size_t *len, u8 *mac, unsigned int mdlen) { - HMAC_CTX ctx; + HMAC_CTX *ctx; size_t i; int res; - HMAC_CTX_init(&ctx); -#if OPENSSL_VERSION_NUMBER < 0x00909000 - HMAC_Init_ex(&ctx, key, key_len, type, NULL); -#else /* openssl < 0.9.9 */ - if (HMAC_Init_ex(&ctx, key, key_len, type, NULL) != 1) + if (TEST_FAIL()) return -1; -#endif /* openssl < 0.9.9 */ + + ctx = HMAC_CTX_new(); + if (!ctx) + return -1; + res = HMAC_Init_ex(ctx, key, key_len, type, NULL); + if (res != 1) + goto done; for (i = 0; i < num_elem; i++) - HMAC_Update(&ctx, addr[i], len[i]); + HMAC_Update(ctx, addr[i], len[i]); -#if OPENSSL_VERSION_NUMBER < 0x00909000 - HMAC_Final(&ctx, mac, &mdlen); - res = 1; -#else /* openssl < 0.9.9 */ - res = HMAC_Final(&ctx, mac, &mdlen); -#endif /* openssl < 0.9.9 */ - HMAC_CTX_cleanup(&ctx); + res = HMAC_Final(ctx, mac, &mdlen); +done: + HMAC_CTX_free(ctx); return res == 1 ? 0 : -1; } @@ -892,6 +1045,9 @@ int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem, int ret = -1; size_t outlen, i; + if (TEST_FAIL()) + return -1; + ctx = CMAC_CTX_new(); if (ctx == NULL) return -1; @@ -941,13 +1097,20 @@ int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac) struct crypto_bignum * crypto_bignum_init(void) { + if (TEST_FAIL()) + return NULL; return (struct crypto_bignum *) BN_new(); } struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len) { - BIGNUM *bn = BN_bin2bn(buf, len, NULL); + BIGNUM *bn; + + if (TEST_FAIL()) + return NULL; + + bn = BN_bin2bn(buf, len, NULL); return (struct crypto_bignum *) bn; } @@ -966,6 +1129,9 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a, { int num_bytes, offset; + if (TEST_FAIL()) + return -1; + if (padlen > buflen) return -1; @@ -1019,6 +1185,9 @@ int crypto_bignum_exptmod(const struct crypto_bignum *a, int res; BN_CTX *bnctx; + if (TEST_FAIL()) + return -1; + bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; @@ -1037,6 +1206,8 @@ int crypto_bignum_inverse(const struct crypto_bignum *a, BIGNUM *res; BN_CTX *bnctx; + if (TEST_FAIL()) + return -1; bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; @@ -1052,6 +1223,8 @@ int crypto_bignum_sub(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c) { + if (TEST_FAIL()) + return -1; return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ? 0 : -1; } @@ -1065,6 +1238,9 @@ int crypto_bignum_div(const struct crypto_bignum *a, BN_CTX *bnctx; + if (TEST_FAIL()) + return -1; + bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; @@ -1085,6 +1261,9 @@ int crypto_bignum_mulmod(const struct crypto_bignum *a, BN_CTX *bnctx; + if (TEST_FAIL()) + return -1; + bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; @@ -1128,6 +1307,9 @@ int crypto_bignum_legendre(const struct crypto_bignum *a, BIGNUM *exp = NULL, *tmp = NULL; int res = -2; + if (TEST_FAIL()) + return -2; + bnctx = BN_CTX_new(); if (bnctx == NULL) return -2; @@ -1252,6 +1434,8 @@ void crypto_ec_deinit(struct crypto_ec *e) struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) { + if (TEST_FAIL()) + return NULL; if (e == NULL) return NULL; return (struct crypto_ec_point *) EC_POINT_new(e->group); @@ -1298,6 +1482,9 @@ int crypto_ec_point_to_bin(struct crypto_ec *e, int ret = -1; int len = BN_num_bytes(e->prime); + if (TEST_FAIL()) + return -1; + x_bn = BN_new(); y_bn = BN_new(); @@ -1328,6 +1515,9 @@ struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, EC_POINT *elem; int len = BN_num_bytes(e->prime); + if (TEST_FAIL()) + return NULL; + x = BN_bin2bn(val, len, NULL); y = BN_bin2bn(val + len, len, NULL); elem = EC_POINT_new(e->group); @@ -1355,6 +1545,8 @@ int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, const struct crypto_ec_point *b, struct crypto_ec_point *c) { + if (TEST_FAIL()) + return -1; return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a, (const EC_POINT *) b, e->bnctx) ? 0 : -1; } @@ -1364,6 +1556,8 @@ int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, const struct crypto_bignum *b, struct crypto_ec_point *res) { + if (TEST_FAIL()) + return -1; return EC_POINT_mul(e->group, (EC_POINT *) res, NULL, (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx) ? 0 : -1; @@ -1372,6 +1566,8 @@ int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p) { + if (TEST_FAIL()) + return -1; return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1; } @@ -1380,6 +1576,8 @@ int crypto_ec_point_solve_y_coord(struct crypto_ec *e, struct crypto_ec_point *p, const struct crypto_bignum *x, int y_bit) { + if (TEST_FAIL()) + return -1; if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p, (const BIGNUM *) x, y_bit, e->bnctx) || @@ -1395,6 +1593,9 @@ crypto_ec_point_compute_y_sqr(struct crypto_ec *e, { BIGNUM *tmp, *tmp2, *y_sqr = NULL; + if (TEST_FAIL()) + return NULL; + tmp = BN_new(); tmp2 = BN_new(); diff --git a/src/crypto/dh_group5.c b/src/crypto/dh_group5.c index ccdbfc8129583..425c848acb832 100644 --- a/src/crypto/dh_group5.c +++ b/src/crypto/dh_group5.c @@ -15,6 +15,7 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) { + wpabuf_free(*publ); *publ = dh_init(dh_groups_get(5), priv); if (*publ == NULL) return NULL; diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c index 3aeb2bbc60af3..7912361ff8c6f 100644 --- a/src/crypto/dh_groups.c +++ b/src/crypto/dh_groups.c @@ -1218,14 +1218,19 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv) pv_len = dh->prime_len; pv = wpabuf_alloc(pv_len); - if (pv == NULL) + if (pv == NULL) { + wpabuf_clear_free(*priv); + *priv = NULL; return NULL; + } if (crypto_mod_exp(dh->generator, dh->generator_len, wpabuf_head(*priv), wpabuf_len(*priv), dh->prime, dh->prime_len, wpabuf_mhead(pv), &pv_len) < 0) { wpabuf_clear_free(pv); wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + wpabuf_clear_free(*priv); + *priv = NULL; return NULL; } wpabuf_put(pv, pv_len); diff --git a/src/crypto/fips_prf_openssl.c b/src/crypto/fips_prf_openssl.c index fb03efcd4ffc2..4697e041093a8 100644 --- a/src/crypto/fips_prf_openssl.c +++ b/src/crypto/fips_prf_openssl.c @@ -17,6 +17,19 @@ static void sha1_transform(u32 *state, const u8 data[64]) { SHA_CTX context; os_memset(&context, 0, sizeof(context)); +#if defined(OPENSSL_IS_BORINGSSL) && !defined(ANDROID) + context.h[0] = state[0]; + context.h[1] = state[1]; + context.h[2] = state[2]; + context.h[3] = state[3]; + context.h[4] = state[4]; + SHA1_Transform(&context, data); + state[0] = context.h[0]; + state[1] = context.h[1]; + state[2] = context.h[2]; + state[3] = context.h[3]; + state[4] = context.h[4]; +#else context.h0 = state[0]; context.h1 = state[1]; context.h2 = state[2]; @@ -28,6 +41,7 @@ static void sha1_transform(u32 *state, const u8 data[64]) state[2] = context.h2; state[3] = context.h3; state[4] = context.h4; +#endif } @@ -62,12 +76,11 @@ int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) /* w_i = G(t, XVAL) */ os_memcpy(_t, t, 20); sha1_transform(_t, xkey); - _t[0] = host_to_be32(_t[0]); - _t[1] = host_to_be32(_t[1]); - _t[2] = host_to_be32(_t[2]); - _t[3] = host_to_be32(_t[3]); - _t[4] = host_to_be32(_t[4]); - os_memcpy(xpos, _t, 20); + WPA_PUT_BE32(xpos, _t[0]); + WPA_PUT_BE32(xpos + 4, _t[1]); + WPA_PUT_BE32(xpos + 8, _t[2]); + WPA_PUT_BE32(xpos + 12, _t[3]); + WPA_PUT_BE32(xpos + 16, _t[4]); /* XKEY = (1 + XKEY + w_i) mod 2^b */ carry = 1; diff --git a/src/crypto/md4-internal.c b/src/crypto/md4-internal.c index cd5e6ca8cc883..d9c737a2970b6 100644 --- a/src/crypto/md4-internal.c +++ b/src/crypto/md4-internal.c @@ -31,6 +31,9 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) MD4_CTX ctx; size_t i; + if (TEST_FAIL()) + return -1; + MD4Init(&ctx); for (i = 0; i < num_elem; i++) MD4Update(&ctx, addr[i], len[i]); diff --git a/src/crypto/md5-internal.c b/src/crypto/md5-internal.c index f0a2a5d3a5afa..944698a6328b6 100644 --- a/src/crypto/md5-internal.c +++ b/src/crypto/md5-internal.c @@ -33,6 +33,9 @@ int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) MD5_CTX ctx; size_t i; + if (TEST_FAIL()) + return -1; + MD5Init(&ctx); for (i = 0; i < num_elem; i++) MD5Update(&ctx, addr[i], len[i]); diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c index 053d203cb65bb..d0d6a96af2bc2 100644 --- a/src/crypto/ms_funcs.c +++ b/src/crypto/ms_funcs.c @@ -48,7 +48,7 @@ static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len, WPA_PUT_LE16(ucs2_buffer + j, ((c & 0x1F) << 6) | (c2 & 0x3F)); j += 2; - } else if (i == utf8_string_len || + } else if (i == utf8_string_len - 1 || j >= ucs2_buffer_size - 1) { /* incomplete surrogate */ return -1; diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c index 24bc3ffe1759b..ffcba66af652f 100644 --- a/src/crypto/sha1-internal.c +++ b/src/crypto/sha1-internal.c @@ -33,6 +33,9 @@ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) SHA1_CTX ctx; size_t i; + if (TEST_FAIL()) + return -1; + SHA1Init(&ctx); for (i = 0; i < num_elem; i++) SHA1Update(&ctx, addr[i], len[i]); @@ -294,7 +297,6 @@ void SHA1Final(unsigned char digest[20], SHA1_CTX* context) 255); } /* Wipe variables */ - i = 0; os_memset(context->buffer, 0, 64); os_memset(context->state, 0, 20); os_memset(context->count, 0, 8); diff --git a/src/crypto/sha256-internal.c b/src/crypto/sha256-internal.c index 35299b0524bd9..86a548ee472d1 100644 --- a/src/crypto/sha256-internal.c +++ b/src/crypto/sha256-internal.c @@ -28,6 +28,9 @@ int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, struct sha256_state ctx; size_t i; + if (TEST_FAIL()) + return -1; + sha256_init(&ctx); for (i = 0; i < num_elem; i++) if (sha256_process(&ctx, addr[i], len[i])) diff --git a/src/crypto/sha256-prf.c b/src/crypto/sha256-prf.c index 79791c06cf0bb..722cad6bdeb4d 100644 --- a/src/crypto/sha256-prf.c +++ b/src/crypto/sha256-prf.c @@ -1,6 +1,6 @@ /* * SHA256-based PRF (IEEE 802.11r) - * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,14 +22,16 @@ * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure * * This function is used to derive new, cryptographically separate keys from a * given key. */ -void sha256_prf(const u8 *key, size_t key_len, const char *label, +int sha256_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len) { - sha256_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8); + return sha256_prf_bits(key, key_len, label, data, data_len, buf, + buf_len * 8); } @@ -42,15 +44,16 @@ void sha256_prf(const u8 *key, size_t key_len, const char *label, * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bits of key to generate + * Returns: 0 on success, -1 on failure * * This function is used to derive new, cryptographically separate keys from a * given key. If the requested buf_len is not divisible by eight, the least * significant 1-7 bits of the last octet in the output are not part of the * requested output. */ -void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, - size_t buf_len_bits) +int sha256_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits) { u16 counter = 1; size_t pos, plen; @@ -75,11 +78,14 @@ void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, plen = buf_len - pos; WPA_PUT_LE16(counter_le, counter); if (plen >= SHA256_MAC_LEN) { - hmac_sha256_vector(key, key_len, 4, addr, len, - &buf[pos]); + if (hmac_sha256_vector(key, key_len, 4, addr, len, + &buf[pos]) < 0) + return -1; pos += SHA256_MAC_LEN; } else { - hmac_sha256_vector(key, key_len, 4, addr, len, hash); + if (hmac_sha256_vector(key, key_len, 4, addr, len, + hash) < 0) + return -1; os_memcpy(&buf[pos], hash, plen); pos += plen; break; @@ -97,4 +103,6 @@ void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, } os_memset(hash, 0, sizeof(hash)); + + return 0; } diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h index b15f51158f326..5219022edd7d6 100644 --- a/src/crypto/sha256.h +++ b/src/crypto/sha256.h @@ -1,6 +1,6 @@ /* * SHA256 hash implementation and interface functions - * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -15,11 +15,11 @@ int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); -void sha256_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len); -void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, - size_t buf_len_bits); +int sha256_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +int sha256_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits); void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); diff --git a/src/crypto/sha384-internal.c b/src/crypto/sha384-internal.c new file mode 100644 index 0000000000000..646f72979c2a6 --- /dev/null +++ b/src/crypto/sha384-internal.c @@ -0,0 +1,92 @@ +/* + * SHA-384 hash implementation and interface functions + * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha384_i.h" +#include "crypto.h" + + +/** + * sha384_vector - SHA384 hash for data vector + * @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 + * Returns: 0 on success, -1 of failure + */ +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + struct sha384_state ctx; + size_t i; + + sha384_init(&ctx); + for (i = 0; i < num_elem; i++) + if (sha384_process(&ctx, addr[i], len[i])) + return -1; + if (sha384_done(&ctx, mac)) + return -1; + return 0; +} + + +/* ===== start - public domain SHA384 implementation ===== */ + +/* This is based on SHA384 implementation in LibTomCrypt that was released into + * public domain by Tom St Denis. */ + +#define CONST64(n) n ## ULL + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +void sha384_init(struct sha384_state *md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = CONST64(0xcbbb9d5dc1059ed8); + md->state[1] = CONST64(0x629a292a367cd507); + md->state[2] = CONST64(0x9159015a3070dd17); + md->state[3] = CONST64(0x152fecd8f70e5939); + md->state[4] = CONST64(0x67332667ffc00b31); + md->state[5] = CONST64(0x8eb44a8768581511); + md->state[6] = CONST64(0xdb0c2e0d64f98fa7); + md->state[7] = CONST64(0x47b5481dbefa4fa4); +} + +int sha384_process(struct sha384_state *md, const unsigned char *in, + unsigned long inlen) +{ + return sha512_process(md, in, inlen); +} + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (48 bytes) + @return CRYPT_OK if successful +*/ +int sha384_done(struct sha384_state *md, unsigned char *out) +{ + unsigned char buf[64]; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + if (sha512_done(md, buf) != 0) + return -1; + + os_memcpy(out, buf, 48); + return 0; +} + +/* ===== end - public domain SHA384 implementation ===== */ diff --git a/src/crypto/sha384_i.h b/src/crypto/sha384_i.h new file mode 100644 index 0000000000000..a00253ff2cd0e --- /dev/null +++ b/src/crypto/sha384_i.h @@ -0,0 +1,23 @@ +/* + * SHA-384 internal definitions + * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA384_I_H +#define SHA384_I_H + +#include "sha512_i.h" + +#define SHA384_BLOCK_SIZE SHA512_BLOCK_SIZE + +#define sha384_state sha512_state + +void sha384_init(struct sha384_state *md); +int sha384_process(struct sha384_state *md, const unsigned char *in, + unsigned long inlen); +int sha384_done(struct sha384_state *md, unsigned char *out); + +#endif /* SHA384_I_H */ diff --git a/src/crypto/sha512-internal.c b/src/crypto/sha512-internal.c new file mode 100644 index 0000000000000..76c4fe750b65f --- /dev/null +++ b/src/crypto/sha512-internal.c @@ -0,0 +1,264 @@ +/* + * SHA-512 hash implementation and interface functions + * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com> + * + * 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_i.h" +#include "crypto.h" + + +/** + * sha512_vector - SHA512 hash for data vector + * @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 + * Returns: 0 on success, -1 of failure + */ +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + struct sha512_state ctx; + size_t i; + + sha512_init(&ctx); + for (i = 0; i < num_elem; i++) + if (sha512_process(&ctx, addr[i], len[i])) + return -1; + if (sha512_done(&ctx, mac)) + return -1; + return 0; +} + + +/* ===== start - public domain SHA512 implementation ===== */ + +/* This is based on SHA512 implementation in LibTomCrypt that was released into + * public domain by Tom St Denis. */ + +#define CONST64(n) n ## ULL + +/* the K array */ +static const u64 K[80] = { + CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd), + CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc), + CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019), + CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118), + CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe), + CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2), + CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1), + CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694), + CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3), + CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65), + CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483), + CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5), + CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210), + CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4), + CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725), + CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70), + CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926), + CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df), + CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8), + CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b), + CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001), + CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30), + CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910), + CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8), + CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53), + CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8), + CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb), + CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3), + CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60), + CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec), + CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9), + CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b), + CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207), + CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178), + CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6), + CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b), + CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493), + CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c), + CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a), + CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817) +}; + +/* Various logical functions */ +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) ROR64c(x, n) +#define R(x, n) (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) n)) +#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) +#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) +#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) +#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +#define ROR64c(x, y) \ + ( ((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) (y) & CONST64(63))) | \ + ((x) << ((u64) (64 - ((y) & CONST64(63)))))) & \ + CONST64(0xFFFFFFFFFFFFFFFF)) + +/* compress 1024-bits */ +static int sha512_compress(struct sha512_state *md, unsigned char *buf) +{ + u64 S[8], W[80], t0, t1; + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + + /* copy the state into 1024-bits into W[0..15] */ + for (i = 0; i < 16; i++) + W[i] = WPA_GET_BE64(buf + (8 * i)); + + /* fill W[16..79] */ + for (i = 16; i < 80; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + + W[i - 16]; + } + + /* Compress */ + for (i = 0; i < 80; i++) { + t0 = S[7] + Sigma1(S[4]) + Ch(S[4], S[5], S[6]) + K[i] + W[i]; + t1 = Sigma0(S[0]) + Maj(S[0], S[1], S[2]); + S[7] = S[6]; + S[6] = S[5]; + S[5] = S[4]; + S[4] = S[3] + t0; + S[3] = S[2]; + S[2] = S[1]; + S[1] = S[0]; + S[0] = t0 + t1; + } + + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + + return 0; +} + + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +void sha512_init(struct sha512_state *md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = CONST64(0x6a09e667f3bcc908); + md->state[1] = CONST64(0xbb67ae8584caa73b); + md->state[2] = CONST64(0x3c6ef372fe94f82b); + md->state[3] = CONST64(0xa54ff53a5f1d36f1); + md->state[4] = CONST64(0x510e527fade682d1); + md->state[5] = CONST64(0x9b05688c2b3e6c1f); + md->state[6] = CONST64(0x1f83d9abfb41bd6b); + md->state[7] = CONST64(0x5be0cd19137e2179); +} + + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +int sha512_process(struct sha512_state *md, const unsigned char *in, + unsigned long inlen) +{ + unsigned long n; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + while (inlen > 0) { + if (md->curlen == 0 && inlen >= SHA512_BLOCK_SIZE) { + if (sha512_compress(md, (unsigned char *) in) < 0) + return -1; + md->length += SHA512_BLOCK_SIZE * 8; + in += SHA512_BLOCK_SIZE; + inlen -= SHA512_BLOCK_SIZE; + } else { + n = MIN(inlen, (SHA512_BLOCK_SIZE - md->curlen)); + os_memcpy(md->buf + md->curlen, in, n); + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == SHA512_BLOCK_SIZE) { + if (sha512_compress(md, md->buf) < 0) + return -1; + md->length += 8 * SHA512_BLOCK_SIZE; + md->curlen = 0; + } + } + } + + return 0; +} + + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (64 bytes) + @return CRYPT_OK if successful +*/ +int sha512_done(struct sha512_state *md, unsigned char *out) +{ + int i; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + /* increase the length of the message */ + md->length += md->curlen * CONST64(8); + + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char) 0x80; + + /* if the length is currently above 112 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 112) { + while (md->curlen < 128) { + md->buf[md->curlen++] = (unsigned char) 0; + } + sha512_compress(md, md->buf); + md->curlen = 0; + } + + /* pad up to 120 bytes of zeroes + * note: that from 112 to 120 is the 64 MSB of the length. We assume + * that you won't hash > 2^64 bits of data... :-) + */ + while (md->curlen < 120) { + md->buf[md->curlen++] = (unsigned char) 0; + } + + /* store length */ + WPA_PUT_BE64(md->buf + 120, md->length); + sha512_compress(md, md->buf); + + /* copy output */ + for (i = 0; i < 8; i++) + WPA_PUT_BE64(out + (8 * i), md->state[i]); + + return 0; +} + +/* ===== end - public domain SHA512 implementation ===== */ diff --git a/src/crypto/sha512_i.h b/src/crypto/sha512_i.h new file mode 100644 index 0000000000000..108958911ef14 --- /dev/null +++ b/src/crypto/sha512_i.h @@ -0,0 +1,25 @@ +/* + * SHA-512 internal definitions + * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA512_I_H +#define SHA512_I_H + +#define SHA512_BLOCK_SIZE 128 + +struct sha512_state { + u64 length, state[8]; + u32 curlen; + u8 buf[SHA512_BLOCK_SIZE]; +}; + +void sha512_init(struct sha512_state *md); +int sha512_process(struct sha512_state *md, const unsigned char *in, + unsigned long inlen); +int sha512_done(struct sha512_state *md, unsigned char *out); + +#endif /* SHA512_I_H */ diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 2e562339cc5c0..11d504a97fc05 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -95,6 +95,8 @@ struct tls_config { #define TLS_CONN_DISABLE_TLSv1_2 BIT(6) #define TLS_CONN_EAP_FAST BIT(7) #define TLS_CONN_DISABLE_TLSv1_0 BIT(8) +#define TLS_CONN_EXT_CERT_CHECK BIT(9) +#define TLS_CONN_REQUIRE_OCSP_ALL BIT(10) /** * struct tls_connection_params - Parameters for TLS connection @@ -139,6 +141,9 @@ struct tls_config { * @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 * * TLS connection parameters to be configured with tls_connection_set_params() * and tls_global_set_params(). @@ -179,6 +184,7 @@ struct tls_connection_params { unsigned int flags; const char *ocsp_stapling_response; + const char *ocsp_stapling_response_multi; }; @@ -330,29 +336,36 @@ int __must_check tls_connection_get_random(void *tls_ctx, struct tls_random *data); /** - * tls_connection_prf - Use TLS-PRF to derive keying material + * tls_connection_export_key - Derive keying material from a TLS connection * @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 - * @server_random_first: seed is 0 = client_random|server_random, - * 1 = server_random|client_random - * @skip_keyblock: Skip TLS key block from the beginning of PRF output * @out: Buffer for output data from TLS-PRF * @out_len: Length of the output buffer * Returns: 0 on success, -1 on failure * - * tls_connection_prf() is required so that further keying material can be - * derived from the master secret. Example implementation of this function is in - * tls_prf_sha1_md5() when it is called with seed set to - * client_random|server_random (or server_random|client_random). For TLSv1.2 and - * newer, a different PRF is needed, though. + * Exports keying material using the mechanism described in RFC 5705. */ -int __must_check tls_connection_prf(void *tls_ctx, - struct tls_connection *conn, - const char *label, - int server_random_first, - int skip_keyblock, - u8 *out, size_t out_len); +int __must_check tls_connection_export_key(void *tls_ctx, + struct tls_connection *conn, + const char *label, + u8 *out, size_t out_len); + +/** + * tls_connection_get_eap_fast_key - Derive key material for EAP-FAST + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @out: Buffer for output data from TLS-PRF + * @out_len: Length of the output buffer + * Returns: 0 on success, -1 on failure + * + * Exports key material after the normal TLS key block for use with + * EAP-FAST. Most callers will want tls_connection_export_key(), but EAP-FAST + * uses a different legacy mechanism. + */ +int __must_check tls_connection_get_eap_fast_key(void *tls_ctx, + struct tls_connection *conn, + u8 *out, size_t out_len); /** * tls_connection_handshake - Process TLS handshake (client side) @@ -455,7 +468,9 @@ enum { TLS_CIPHER_RC4_SHA /* 0x0005 */, TLS_CIPHER_AES128_SHA /* 0x002f */, TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */, - TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */ + TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */, + TLS_CIPHER_RSA_DHE_AES256_SHA /* 0x0039 */, + TLS_CIPHER_AES256_SHA /* 0x0035 */, }; /** diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c index f994379b16b22..200f0eda931af 100644 --- a/src/crypto/tls_gnutls.c +++ b/src/crypto/tls_gnutls.c @@ -37,6 +37,8 @@ struct tls_global { union tls_event_data *data); void *cb_ctx; int cert_in_cb; + + char *ocsp_stapling_response; }; struct tls_connection { @@ -133,6 +135,7 @@ void tls_deinit(void *ssl_ctx) if (global->params_set) gnutls_certificate_free_credentials(global->xcred); os_free(global->session_data); + os_free(global->ocsp_stapling_response); os_free(global); } @@ -347,6 +350,18 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (conn == NULL || params == NULL) return -1; + if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) { + wpa_printf(MSG_INFO, + "GnuTLS: ocsp=3 not supported"); + return -1; + } + + if (params->flags & TLS_CONN_EXT_CERT_CHECK) { + wpa_printf(MSG_INFO, + "GnuTLS: tls_ext_cert_check=1 not supported"); + return -1; + } + if (params->subject_match) { wpa_printf(MSG_INFO, "GnuTLS: subject_match not supported"); return -1; @@ -596,6 +611,44 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } +#if GNUTLS_VERSION_NUMBER >= 0x030103 +static int server_ocsp_status_req(gnutls_session_t session, void *ptr, + gnutls_datum_t *resp) +{ + struct tls_global *global = ptr; + char *cached; + size_t len; + + if (!global->ocsp_stapling_response) { + wpa_printf(MSG_DEBUG, "GnuTLS: OCSP status callback - no response configured"); + return GNUTLS_E_NO_CERTIFICATE_STATUS; + } + + cached = os_readfile(global->ocsp_stapling_response, &len); + if (!cached) { + wpa_printf(MSG_DEBUG, + "GnuTLS: OCSP status callback - could not read response file (%s)", + global->ocsp_stapling_response); + return GNUTLS_E_NO_CERTIFICATE_STATUS; + } + + wpa_printf(MSG_DEBUG, + "GnuTLS: OCSP status callback - send cached response"); + resp->data = gnutls_malloc(len); + if (!resp->data) { + os_free(resp); + return GNUTLS_E_MEMORY_ERROR; + } + + os_memcpy(resp->data, cached, len); + resp->size = len; + os_free(cached); + + return GNUTLS_E_SUCCESS; +} +#endif /* 3.1.3 */ + + int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { @@ -690,6 +743,17 @@ int tls_global_set_params(void *tls_ctx, } } +#if GNUTLS_VERSION_NUMBER >= 0x030103 + os_free(global->ocsp_stapling_response); + if (params->ocsp_stapling_response) + global->ocsp_stapling_response = + os_strdup(params->ocsp_stapling_response); + else + global->ocsp_stapling_response = NULL; + gnutls_certificate_set_ocsp_status_request_function( + global->xcred, server_ocsp_status_req, global); +#endif /* 3.1.3 */ + global->params_set = 1; return 0; @@ -746,15 +810,22 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, } -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - int skip_keyblock, u8 *out, size_t out_len) +int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, + const char *label, u8 *out, size_t out_len) { - if (conn == NULL || conn->session == NULL || skip_keyblock) + if (conn == NULL || conn->session == NULL) return -1; return gnutls_prf(conn->session, os_strlen(label), label, - server_random_first, 0, NULL, out_len, (char *) out); + 0 /* client_random first */, 0, NULL, out_len, + (char *) out); +} + + +int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, + u8 *out, size_t out_len) +{ + return -1; } diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c index 704751d308fcf..c7cb5ded331f2 100644 --- a/src/crypto/tls_internal.c +++ b/src/crypto/tls_internal.c @@ -23,6 +23,11 @@ struct tls_global { int server; struct tlsv1_credentials *server_cred; int check_crl; + + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; + int cert_in_cb; }; struct tls_connection { @@ -51,6 +56,11 @@ void * tls_init(const struct tls_config *conf) global = os_zalloc(sizeof(*global)); if (global == NULL) return NULL; + if (conf) { + global->event_cb = conf->event_cb; + global->cb_ctx = conf->cb_ctx; + global->cert_in_cb = conf->cert_in_cb; + } return global; } @@ -64,10 +74,12 @@ void tls_deinit(void *ssl_ctx) tlsv1_client_global_deinit(); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER - tlsv1_cred_free(global->server_cred); tlsv1_server_global_deinit(); #endif /* CONFIG_TLS_INTERNAL_SERVER */ } +#ifdef CONFIG_TLS_INTERNAL_SERVER + tlsv1_cred_free(global->server_cred); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ os_free(global); } @@ -95,6 +107,8 @@ struct tls_connection * tls_connection_init(void *tls_ctx) os_free(conn); return NULL; } + tlsv1_client_set_cb(conn->client, global->event_cb, + global->cb_ctx, global->cert_in_cb); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER @@ -186,6 +200,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (conn->client == NULL) return -1; + if (params->flags & TLS_CONN_EXT_CERT_CHECK) { + wpa_printf(MSG_INFO, + "TLS: tls_ext_cert_check=1 not supported"); + return -1; + } + cred = tlsv1_cred_alloc(); if (cred == NULL) return -1; @@ -259,8 +279,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } - tlsv1_client_set_time_checks( - conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS)); + tlsv1_client_set_flags(conn->client, params->flags); return 0; #else /* CONFIG_TLS_INTERNAL_CLIENT */ @@ -312,6 +331,13 @@ int tls_global_set_params(void *tls_ctx, return -1; } + if (params->ocsp_stapling_response) + cred->ocsp_stapling_response = + os_strdup(params->ocsp_stapling_response); + if (params->ocsp_stapling_response_multi) + cred->ocsp_stapling_response_multi = + os_strdup(params->ocsp_stapling_response_multi); + return 0; #else /* CONFIG_TLS_INTERNAL_SERVER */ return -1; @@ -368,9 +394,9 @@ static int tls_get_keyblock_size(struct tls_connection *conn) } -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - int skip_keyblock, u8 *out, size_t out_len) +static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, + const char *label, int server_random_first, + int skip_keyblock, u8 *out, size_t out_len) { int ret = -1, skip = 0; u8 *tmp_out = NULL; @@ -390,14 +416,14 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, if (conn->client) { ret = tlsv1_client_prf(conn->client, label, server_random_first, - _out, out_len); + _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, - _out, out_len); + _out, skip + out_len); } #endif /* CONFIG_TLS_INTERNAL_SERVER */ if (ret == 0 && skip_keyblock) @@ -408,6 +434,21 @@ 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) +{ + return tls_connection_prf(tls_ctx, conn, label, 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); +} + + struct wpabuf * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, @@ -621,7 +662,12 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, int tls_get_version(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { - /* TODO */ + if (conn == NULL) + return -1; +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_get_version(conn->client, buf, buflen); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ return -1; } diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c index ae392ad8aa0f5..dd5681e9ca3c3 100644 --- a/src/crypto/tls_none.c +++ b/src/crypto/tls_none.c @@ -86,9 +86,15 @@ int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, } -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - int skip_keyblock, u8 *out, size_t out_len) +int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, + const char *label, u8 *out, size_t out_len) +{ + return -1; +} + + +int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, + u8 *out, size_t out_len) { return -1; } diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 8b7b47bc256d5..23ac64b48cd9b 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -18,6 +18,7 @@ #include <openssl/ssl.h> #include <openssl/err.h> +#include <openssl/opensslv.h> #include <openssl/pkcs12.h> #include <openssl/x509v3.h> #ifndef OPENSSL_NO_ENGINE @@ -35,12 +36,12 @@ #include "sha1.h" #include "sha256.h" #include "tls.h" +#include "tls_openssl.h" -#if OPENSSL_VERSION_NUMBER < 0x10000000L -/* ERR_remove_thread_state replaces ERR_remove_state and the latter is - * deprecated. However, OpenSSL 0.9.8 doesn't include - * ERR_remove_thread_state. */ -#define ERR_remove_thread_state(tid) ERR_remove_state(0) +#if !defined(CONFIG_FIPS) && \ + (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \ + defined(EAP_SERVER_FAST)) +#define OPENSSL_NEED_EAP_FAST_PRF #endif #if defined(OPENSSL_IS_BORINGSSL) @@ -57,6 +58,51 @@ typedef int stack_index_t; #endif /* OPENSSL_NO_TLSEXT */ #endif /* SSL_set_tlsext_status_type */ +#if (OPENSSL_VERSION_NUMBER < 0x10100000L || \ + defined(LIBRESSL_VERSION_NUMBER)) && \ + !defined(BORINGSSL_API_VERSION) +/* + * SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL + * 1.1.0 and newer BoringSSL revisions. Provide compatibility wrappers for + * older versions. + */ + +static size_t SSL_get_client_random(const SSL *ssl, unsigned char *out, + size_t outlen) +{ + if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE) + return 0; + os_memcpy(out, ssl->s3->client_random, SSL3_RANDOM_SIZE); + return SSL3_RANDOM_SIZE; +} + + +static size_t SSL_get_server_random(const SSL *ssl, unsigned char *out, + size_t outlen) +{ + if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE) + return 0; + os_memcpy(out, ssl->s3->server_random, SSL3_RANDOM_SIZE); + return SSL3_RANDOM_SIZE; +} + + +#ifdef OPENSSL_NEED_EAP_FAST_PRF +static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, + unsigned char *out, size_t outlen) +{ + if (!session || session->master_key_length < 0 || + (size_t) session->master_key_length > outlen) + return 0; + if ((size_t) session->master_key_length < outlen) + outlen = session->master_key_length; + os_memcpy(out, session->master_key, outlen); + return outlen; +} +#endif /* OPENSSL_NEED_EAP_FAST_PRF */ + +#endif + #ifdef ANDROID #include <openssl/pem.h> #include <keystore/keystore_get.h> @@ -71,6 +117,66 @@ static BIO * BIO_from_keystore(const char *key) free(value); return bio; } + + +static int tls_add_ca_from_keystore(X509_STORE *ctx, const char *key_alias) +{ + BIO *bio = BIO_from_keystore(key_alias); + STACK_OF(X509_INFO) *stack = NULL; + stack_index_t i; + + if (bio) { + stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); + BIO_free(bio); + } + + if (!stack) { + wpa_printf(MSG_WARNING, "TLS: Failed to parse certificate: %s", + key_alias); + return -1; + } + + for (i = 0; i < sk_X509_INFO_num(stack); ++i) { + X509_INFO *info = sk_X509_INFO_value(stack, i); + + if (info->x509) + X509_STORE_add_cert(ctx, info->x509); + if (info->crl) + X509_STORE_add_crl(ctx, info->crl); + } + + sk_X509_INFO_pop_free(stack, X509_INFO_free); + + return 0; +} + + +static int tls_add_ca_from_keystore_encoded(X509_STORE *ctx, + const char *encoded_key_alias) +{ + int rc = -1; + int len = os_strlen(encoded_key_alias); + unsigned char *decoded_alias; + + if (len & 1) { + wpa_printf(MSG_WARNING, "Invalid hex-encoded alias: %s", + encoded_key_alias); + return rc; + } + + decoded_alias = os_malloc(len / 2 + 1); + if (decoded_alias) { + if (!hexstr2bin(encoded_key_alias, decoded_alias, len / 2)) { + decoded_alias[len / 2] = '\0'; + rc = tls_add_ca_from_keystore( + ctx, (const char *) decoded_alias); + } + os_free(decoded_alias); + } + + return rc; +} + #endif /* ANDROID */ static int tls_openssl_ref_count = 0; @@ -97,7 +203,7 @@ struct tls_connection { SSL_CTX *ssl_ctx; SSL *ssl; BIO *ssl_in, *ssl_out; -#ifndef OPENSSL_NO_ENGINE +#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) ENGINE *engine; /* functional reference to the engine */ EVP_PKEY *private_key; /* the private key if using engine */ #endif /* OPENSSL_NO_ENGINE */ @@ -125,10 +231,8 @@ struct tls_connection { X509 *peer_issuer; X509 *peer_issuer_issuer; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L unsigned char client_random[SSL3_RANDOM_SIZE]; unsigned char server_random[SSL3_RANDOM_SIZE]; -#endif }; @@ -526,7 +630,8 @@ static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for " "system certificate store: subject='%s'", buf); - if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), + cert)) { tls_show_errors(MSG_WARNING, __func__, "Failed to add ca_cert to OpenSSL " "certificate store"); @@ -624,10 +729,16 @@ static int tls_engine_load_dynamic_generic(const char *pre[], engine = ENGINE_by_id(id); if (engine) { - ENGINE_free(engine); wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already " "available", id); - return 0; + /* + * If it was auto-loaded by ENGINE_by_id() we might still + * need to tell it which PKCS#11 module to use in legacy + * (non-p11-kit) environments. Do so now; even if it was + * properly initialised before, setting it again will be + * harmless. + */ + goto found; } ERR_clear_error(); @@ -664,7 +775,7 @@ static int tls_engine_load_dynamic_generic(const char *pre[], id, ERR_error_string(ERR_get_error(), NULL)); return -1; } - + found: while (post && post[0]) { wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]); if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) { @@ -808,6 +919,7 @@ void * tls_init(const struct tls_config *conf) } #endif /* OPENSSL_FIPS */ #endif /* CONFIG_FIPS */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_load_error_strings(); SSL_library_init(); #ifndef OPENSSL_NO_SHA256 @@ -829,6 +941,7 @@ void * tls_init(const struct tls_config *conf) #endif /* OPENSSL_NO_RC2 */ PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ +#endif /* < 1.1.0 */ } else { context = tls_context_new(conf); if (context == NULL) @@ -849,6 +962,7 @@ void * tls_init(const struct tls_config *conf) os_free(tls_global); tls_global = NULL; } + os_free(data); return NULL; } data->ssl = ssl; @@ -929,6 +1043,7 @@ void tls_deinit(void *ssl_ctx) tls_openssl_ref_count--; if (tls_openssl_ref_count == 0) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L #ifndef OPENSSL_NO_ENGINE ENGINE_cleanup(); #endif /* OPENSSL_NO_ENGINE */ @@ -936,6 +1051,7 @@ void tls_deinit(void *ssl_ctx) ERR_remove_thread_state(NULL); ERR_free_strings(); EVP_cleanup(); +#endif /* < 1.1.0 */ os_free(tls_global->ocsp_stapling_response); tls_global->ocsp_stapling_response = NULL; os_free(tls_global); @@ -967,10 +1083,32 @@ static int tls_is_pin_error(unsigned int err) #endif /* OPENSSL_NO_ENGINE */ +#ifdef ANDROID +/* EVP_PKEY_from_keystore comes from system/security/keystore-engine. */ +EVP_PKEY * EVP_PKEY_from_keystore(const char *key_id); +#endif /* ANDROID */ + static int tls_engine_init(struct tls_connection *conn, const char *engine_id, const char *pin, const char *key_id, const char *cert_id, const char *ca_cert_id) { +#if defined(ANDROID) && defined(OPENSSL_IS_BORINGSSL) +#if !defined(OPENSSL_NO_ENGINE) +#error "This code depends on OPENSSL_NO_ENGINE being defined by BoringSSL." +#endif + if (!key_id) + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + conn->engine = NULL; + conn->private_key = EVP_PKEY_from_keystore(key_id); + if (!conn->private_key) { + wpa_printf(MSG_ERROR, + "ENGINE: cannot load private key with id '%s' [%s]", + key_id, + ERR_error_string(ERR_get_error(), NULL)); + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + } +#endif /* ANDROID && OPENSSL_IS_BORINGSSL */ + #ifndef OPENSSL_NO_ENGINE int ret = -1; if (engine_id == NULL) { @@ -1068,17 +1206,19 @@ err: static void tls_engine_deinit(struct tls_connection *conn) { -#ifndef OPENSSL_NO_ENGINE +#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) wpa_printf(MSG_DEBUG, "ENGINE: engine deinit"); if (conn->private_key) { EVP_PKEY_free(conn->private_key); conn->private_key = NULL; } if (conn->engine) { +#if !defined(OPENSSL_IS_BORINGSSL) ENGINE_finish(conn->engine); +#endif /* !OPENSSL_IS_BORINGSSL */ conn->engine = NULL; } -#endif /* OPENSSL_NO_ENGINE */ +#endif /* ANDROID || !OPENSSL_NO_ENGINE */ } @@ -1097,14 +1237,83 @@ int tls_get_errors(void *ssl_ctx) } +static const char * openssl_content_type(int content_type) +{ + switch (content_type) { + case 20: + return "change cipher spec"; + case 21: + return "alert"; + case 22: + return "handshake"; + case 23: + return "application data"; + case 24: + return "heartbeat"; + case 256: + return "TLS header info"; /* pseudo content type */ + default: + return "?"; + } +} + + +static const char * openssl_handshake_type(int content_type, const u8 *buf, + size_t len) +{ + if (content_type != 22 || !buf || len == 0) + return ""; + switch (buf[0]) { + case 0: + return "hello request"; + case 1: + return "client hello"; + case 2: + return "server hello"; + case 4: + return "new session ticket"; + case 11: + return "certificate"; + case 12: + return "server key exchange"; + case 13: + return "certificate request"; + case 14: + return "server hello done"; + case 15: + return "certificate verify"; + case 16: + return "client key exchange"; + case 20: + return "finished"; + case 21: + return "certificate url"; + case 22: + return "certificate status"; + default: + return "?"; + } +} + + static void tls_msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg) { struct tls_connection *conn = arg; const u8 *pos = buf; - wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d", - write_p ? "TX" : "RX", version, content_type); + if (write_p == 2) { + wpa_printf(MSG_DEBUG, + "OpenSSL: session ver=0x%x content_type=%d", + version, content_type); + wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Data", buf, len); + return; + } + + wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d (%s/%s)", + write_p ? "TX" : "RX", version, content_type, + openssl_content_type(content_type), + openssl_handshake_type(content_type, buf, len)); wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Message", buf, len); if (content_type == 24 && len >= 3 && pos[0] == 1) { size_t payload_len = WPA_GET_BE16(pos + 1); @@ -1234,6 +1443,8 @@ static int tls_match_altsubject_component(X509 *cert, int type, found++; } + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); + return found; } @@ -1346,9 +1557,11 @@ static int tls_match_suffix(X509 *cert, const char *match, int full) 1) { wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", full ? "Match" : "Suffix match"); + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); return 1; } } + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); if (dns_name) { wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched"); @@ -1489,7 +1702,8 @@ static void openssl_tls_cert_event(struct tls_connection *conn, return; os_memset(&ev, 0, sizeof(ev)); - if (conn->cert_probe || context->cert_in_cb) { + if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) || + context->cert_in_cb) { cert = get_x509_cert(err_cert); ev.peer_cert.cert = cert; } @@ -1544,6 +1758,7 @@ static void openssl_tls_cert_event(struct tls_connection *conn, pos += gen->d.ia5->length; *pos = '\0'; } + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); for (alt = 0; alt < num_altsubject; alt++) ev.peer_cert.altsubject[alt] = altsubject[alt]; @@ -1701,7 +1916,33 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) TLS_FAIL_SERVER_CHAIN_PROBE); } - if (preverify_ok && context->event_cb != NULL) +#ifdef OPENSSL_IS_BORINGSSL + if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) && + preverify_ok) { + enum ocsp_result res; + + res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert, + conn->peer_issuer, + conn->peer_issuer_issuer); + if (res == OCSP_REVOKED) { + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "certificate revoked", + TLS_FAIL_REVOKED); + if (err == X509_V_OK) + X509_STORE_CTX_set_error( + x509_ctx, X509_V_ERR_CERT_REVOKED); + } else if (res != OCSP_GOOD && + (conn->flags & TLS_CONN_REQUIRE_OCSP)) { + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "bad certificate status response", + TLS_FAIL_UNSPECIFIED); + } + } +#endif /* OPENSSL_IS_BORINGSSL */ + + if (depth == 0 && preverify_ok && context->event_cb != NULL) context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_SUCCESS, NULL); @@ -1837,30 +2078,40 @@ static int tls_connection_ca_cert(struct tls_data *data, } #ifdef ANDROID + /* Single alias */ if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) { - BIO *bio = BIO_from_keystore(&ca_cert[11]); - STACK_OF(X509_INFO) *stack = NULL; - stack_index_t i; - - if (bio) { - stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); - BIO_free(bio); - } - if (!stack) + if (tls_add_ca_from_keystore(SSL_CTX_get_cert_store(ssl_ctx), + &ca_cert[11]) < 0) return -1; + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + } - for (i = 0; i < sk_X509_INFO_num(stack); ++i) { - X509_INFO *info = sk_X509_INFO_value(stack, i); - if (info->x509) { - X509_STORE_add_cert(ssl_ctx->cert_store, - info->x509); - } - if (info->crl) { - X509_STORE_add_crl(ssl_ctx->cert_store, - info->crl); + /* Multiple aliases separated by space */ + if (ca_cert && os_strncmp("keystores://", ca_cert, 12) == 0) { + char *aliases = os_strdup(&ca_cert[12]); + const char *delim = " "; + int rc = 0; + char *savedptr; + char *alias; + + if (!aliases) + return -1; + alias = strtok_r(aliases, delim, &savedptr); + for (; alias; alias = strtok_r(NULL, delim, &savedptr)) { + if (tls_add_ca_from_keystore_encoded( + SSL_CTX_get_cert_store(ssl_ctx), alias)) { + wpa_printf(MSG_WARNING, + "OpenSSL: %s - Failed to add ca_cert %s from keystore", + __func__, alias); + rc = -1; + break; } } - sk_X509_INFO_pop_free(stack, X509_INFO_free); + os_free(aliases); + if (rc) + return rc; + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); return 0; } @@ -2082,6 +2333,17 @@ static int tls_connection_client_cert(struct tls_connection *conn, if (client_cert == NULL && client_cert_blob == NULL) return 0; +#ifdef PKCS12_FUNCS +#if OPENSSL_VERSION_NUMBER < 0x10002000L + /* + * Clear previously set extra chain certificates, if any, from PKCS#12 + * processing in tls_parse_pkcs12() to allow OpenSSL to build a new + * chain properly. + */ + SSL_CTX_clear_extra_chain_certs(conn->ssl_ctx); +#endif /* OPENSSL_VERSION_NUMBER < 0x10002000L */ +#endif /* PKCS12_FUNCS */ + if (client_cert_blob && SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob, client_cert_blob_len) == 1) { @@ -2229,28 +2491,42 @@ static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, } if (certs) { -#if OPENSSL_VERSION_NUMBER >= 0x10002000L - SSL_clear_chain_certs(ssl); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER) + if (ssl) + SSL_clear_chain_certs(ssl); + else + SSL_CTX_clear_chain_certs(data->ssl); while ((cert = sk_X509_pop(certs)) != NULL) { X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "TLS: additional certificate" " from PKCS12: subject='%s'", buf); - if (SSL_add1_chain_cert(ssl, cert) != 1) { + if ((ssl && SSL_add1_chain_cert(ssl, cert) != 1) || + (!ssl && SSL_CTX_add1_chain_cert(data->ssl, + cert) != 1)) { tls_show_errors(MSG_DEBUG, __func__, "Failed to add additional certificate"); res = -1; + X509_free(cert); break; } + X509_free(cert); } if (!res) { /* Try to continue anyway */ } - sk_X509_free(certs); + sk_X509_pop_free(certs, X509_free); #ifndef OPENSSL_IS_BORINGSSL - res = SSL_build_cert_chain(ssl, - SSL_BUILD_CHAIN_FLAG_CHECK | - SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR); + if (ssl) + res = SSL_build_cert_chain( + ssl, + SSL_BUILD_CHAIN_FLAG_CHECK | + SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR); + else + res = SSL_CTX_build_cert_chain( + data->ssl, + SSL_BUILD_CHAIN_FLAG_CHECK | + SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR); if (!res) { tls_show_errors(MSG_DEBUG, __func__, "Failed to build certificate chain"); @@ -2265,9 +2541,7 @@ static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, */ res = 0; #else /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ -#if OPENSSL_VERSION_NUMBER >= 0x10001000L SSL_CTX_clear_extra_chain_certs(data->ssl); -#endif /* OPENSSL_VERSION_NUMBER >= 0x10001000L */ while ((cert = sk_X509_pop(certs)) != NULL) { X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); @@ -2279,11 +2553,12 @@ static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, */ if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1) { + X509_free(cert); res = -1; break; } } - sk_X509_free(certs); + sk_X509_pop_free(certs, X509_free); #endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ } @@ -2463,7 +2738,7 @@ static int tls_connection_engine_ca_cert(struct tls_data *data, static int tls_connection_engine_private_key(struct tls_connection *conn) { -#ifndef OPENSSL_NO_ENGINE +#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) { tls_show_errors(MSG_ERROR, __func__, "ENGINE: cannot use private key for TLS"); @@ -2812,16 +3087,6 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, if (conn == NULL || keys == NULL) return -1; ssl = conn->ssl; -#if OPENSSL_VERSION_NUMBER < 0x10100000L - if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) - return -1; - - os_memset(keys, 0, sizeof(*keys)); - keys->client_random = ssl->s3->client_random; - keys->client_random_len = SSL3_RANDOM_SIZE; - keys->server_random = ssl->s3->server_random; - keys->server_random_len = SSL3_RANDOM_SIZE; -#else if (ssl == NULL) return -1; @@ -2832,16 +3097,15 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, keys->server_random = conn->server_random; keys->server_random_len = SSL_get_server_random( ssl, conn->server_random, sizeof(conn->server_random)); -#endif return 0; } -#ifndef CONFIG_FIPS +#ifdef OPENSSL_NEED_EAP_FAST_PRF static int openssl_get_keyblock_size(SSL *ssl) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) const EVP_CIPHER *c; const EVP_MD *h; int md_size; @@ -2851,17 +3115,11 @@ static int openssl_get_keyblock_size(SSL *ssl) return -1; c = ssl->enc_read_ctx->cipher; -#if OPENSSL_VERSION_NUMBER >= 0x00909000L h = EVP_MD_CTX_md(ssl->read_hash); -#else - h = ssl->read_hash; -#endif if (h) md_size = EVP_MD_size(h); -#if OPENSSL_VERSION_NUMBER >= 0x10000000L else if (ssl->s3) md_size = ssl->s3->tmp.new_mac_secret_size; -#endif else return -1; @@ -2899,86 +3157,24 @@ static int openssl_get_keyblock_size(SSL *ssl) EVP_CIPHER_iv_length(c)); #endif } -#endif /* CONFIG_FIPS */ +#endif /* OPENSSL_NEED_EAP_FAST_PRF */ -static int openssl_tls_prf(struct tls_connection *conn, - const char *label, int server_random_first, - int skip_keyblock, u8 *out, size_t out_len) +int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, + const char *label, u8 *out, size_t out_len) { -#ifdef CONFIG_FIPS - wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS " - "mode"); - return -1; -#else /* CONFIG_FIPS */ -#if OPENSSL_VERSION_NUMBER < 0x10100000L - SSL *ssl; - u8 *rnd; - int ret = -1; - int skip = 0; - u8 *tmp_out = NULL; - u8 *_out = out; - const char *ver; - - /* - * TLS library did not support key generation, so get the needed TLS - * session parameters and use an internal implementation of TLS PRF to - * derive the key. - */ - - if (conn == NULL) - return -1; - ssl = conn->ssl; - if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL || - ssl->session->master_key_length <= 0) - return -1; - ver = SSL_get_version(ssl); - - if (skip_keyblock) { - skip = openssl_get_keyblock_size(ssl); - if (skip < 0) - return -1; - tmp_out = os_malloc(skip + out_len); - if (!tmp_out) - return -1; - _out = tmp_out; - } - - rnd = os_malloc(2 * SSL3_RANDOM_SIZE); - if (!rnd) { - os_free(tmp_out); + if (!conn || + SSL_export_keying_material(conn->ssl, out, out_len, label, + os_strlen(label), NULL, 0, 0) != 1) return -1; - } - - if (server_random_first) { - os_memcpy(rnd, ssl->s3->server_random, SSL3_RANDOM_SIZE); - os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->client_random, - SSL3_RANDOM_SIZE); - } else { - os_memcpy(rnd, ssl->s3->client_random, SSL3_RANDOM_SIZE); - os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->server_random, - SSL3_RANDOM_SIZE); - } + return 0; +} - if (os_strcmp(ver, "TLSv1.2") == 0) { - tls_prf_sha256(ssl->session->master_key, - ssl->session->master_key_length, - label, rnd, 2 * SSL3_RANDOM_SIZE, - _out, skip + out_len); - ret = 0; - } else if (tls_prf_sha1_md5(ssl->session->master_key, - ssl->session->master_key_length, - label, rnd, 2 * SSL3_RANDOM_SIZE, - _out, skip + out_len) == 0) { - ret = 0; - } - os_free(rnd); - if (ret == 0 && skip_keyblock) - os_memcpy(out, _out + skip, out_len); - bin_clear_free(tmp_out, skip); - return ret; -#else +int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, + u8 *out, size_t out_len) +{ +#ifdef OPENSSL_NEED_EAP_FAST_PRF SSL *ssl; SSL_SESSION *sess; u8 *rnd; @@ -2993,9 +3189,9 @@ static int openssl_tls_prf(struct tls_connection *conn, const char *ver; /* - * TLS library did not support key generation, so get the needed TLS - * session parameters and use an internal implementation of TLS PRF to - * derive the key. + * TLS library did not support EAP-FAST key generation, so get the + * needed TLS session parameters and use an internal implementation of + * TLS PRF to derive the key. */ if (conn == NULL) @@ -3008,15 +3204,13 @@ static int openssl_tls_prf(struct tls_connection *conn, if (!ver || !sess) return -1; - if (skip_keyblock) { - skip = openssl_get_keyblock_size(ssl); - if (skip < 0) - return -1; - tmp_out = os_malloc(skip + out_len); - if (!tmp_out) - return -1; - _out = tmp_out; - } + skip = openssl_get_keyblock_size(ssl); + if (skip < 0) + return -1; + tmp_out = os_malloc(skip + out_len); + if (!tmp_out) + return -1; + _out = tmp_out; rnd = os_malloc(2 * SSL3_RANDOM_SIZE); if (!rnd) { @@ -3029,59 +3223,31 @@ static int openssl_tls_prf(struct tls_connection *conn, master_key_len = SSL_SESSION_get_master_key(sess, master_key, sizeof(master_key)); - if (server_random_first) { - os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE); - os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random, - SSL3_RANDOM_SIZE); - } else { - os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE); - os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random, - SSL3_RANDOM_SIZE); - } + os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE); + os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random, SSL3_RANDOM_SIZE); if (os_strcmp(ver, "TLSv1.2") == 0) { tls_prf_sha256(master_key, master_key_len, - label, rnd, 2 * SSL3_RANDOM_SIZE, + "key expansion", rnd, 2 * SSL3_RANDOM_SIZE, _out, skip + out_len); ret = 0; } else if (tls_prf_sha1_md5(master_key, master_key_len, - label, rnd, 2 * SSL3_RANDOM_SIZE, + "key expansion", rnd, 2 * SSL3_RANDOM_SIZE, _out, skip + out_len) == 0) { ret = 0; } os_memset(master_key, 0, sizeof(master_key)); os_free(rnd); - if (ret == 0 && skip_keyblock) + if (ret == 0) os_memcpy(out, _out + skip, out_len); bin_clear_free(tmp_out, skip); return ret; -#endif -#endif /* CONFIG_FIPS */ -} - - -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - int skip_keyblock, u8 *out, size_t out_len) -{ -#if OPENSSL_VERSION_NUMBER >= 0x10001000L - SSL *ssl; - if (conn == NULL) - return -1; - if (server_random_first || skip_keyblock) - return openssl_tls_prf(conn, label, - server_random_first, skip_keyblock, - out, out_len); - ssl = conn->ssl; - if (SSL_export_keying_material(ssl, out, out_len, label, - os_strlen(label), NULL, 0, 0) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF"); - return 0; - } -#endif - return openssl_tls_prf(conn, label, server_random_first, - skip_keyblock, out, out_len); +#else /* OPENSSL_NEED_EAP_FAST_PRF */ + wpa_printf(MSG_ERROR, + "OpenSSL: EAP-FAST keys cannot be exported in FIPS mode"); + return -1; +#endif /* OPENSSL_NEED_EAP_FAST_PRF */ } @@ -3340,18 +3506,14 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx, int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) { -#if OPENSSL_VERSION_NUMBER >= 0x10001000L return conn ? SSL_cache_hit(conn->ssl) : 0; -#else - return conn ? conn->ssl->hit : 0; -#endif } int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers) { - char buf[100], *pos, *end; + char buf[500], *pos, *end; u8 *c; int ret; @@ -3379,6 +3541,12 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, case TLS_CIPHER_ANON_DH_AES128_SHA: suite = "ADH-AES128-SHA"; break; + case TLS_CIPHER_RSA_DHE_AES256_SHA: + suite = "DHE-RSA-AES256-SHA"; + break; + case TLS_CIPHER_AES256_SHA: + suite = "AES256-SHA"; + break; default: wpa_printf(MSG_DEBUG, "TLS: Unsupported " "cipher selection: %d", *c); @@ -3394,7 +3562,7 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1); -#if OPENSSL_VERSION_NUMBER >= 0x10100000L +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) if (os_strstr(buf, ":ADH-")) { /* @@ -3687,10 +3855,12 @@ static int ocsp_resp_cb(SSL *s, void *arg) wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" : " (OCSP not required)"); + OCSP_CERTID_free(id); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1; } + OCSP_CERTID_free(id); if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) { tls_show_errors(MSG_INFO, __func__, @@ -3769,6 +3939,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (conn == NULL) return -1; + if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) { + wpa_printf(MSG_INFO, + "OpenSSL: ocsp=3 not supported"); + return -1; + } + /* * If the engine isn't explicitly configured, and any of the * cert/key fields are actually PKCS#11 URIs, then automatically @@ -3879,6 +4055,11 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, tls_set_conn_flags(conn->ssl, params->flags); +#ifdef OPENSSL_IS_BORINGSSL + if (params->flags & TLS_CONN_REQUEST_OCSP) { + SSL_enable_ocsp_stapling(conn->ssl); + } +#else /* OPENSSL_IS_BORINGSSL */ #ifdef HAVE_OCSP if (params->flags & TLS_CONN_REQUEST_OCSP) { SSL_CTX *ssl_ctx = data->ssl; @@ -3897,6 +4078,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, "OpenSSL: No OCSP support included - allow optional OCSP case to continue"); } #endif /* HAVE_OCSP */ +#endif /* OPENSSL_IS_BORINGSSL */ conn->flags = params->flags; @@ -3964,7 +4146,7 @@ int tls_global_set_params(void *tls_ctx, * commented out unless explicitly needed for EAP-FAST in order to be able to * build this file with unmodified openssl. */ -#ifdef OPENSSL_IS_BORINGSSL +#if (defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, const SSL_CIPHER **cipher, void *arg) @@ -3977,7 +4159,7 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, struct tls_connection *conn = arg; int ret; -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) if (conn == NULL || conn->session_ticket_cb == NULL) return 0; @@ -4072,9 +4254,15 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, int tls_get_library_version(char *buf, size_t buf_len) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) + return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s", + OPENSSL_VERSION_TEXT, + OpenSSL_version(OPENSSL_VERSION)); +#else return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); +#endif } diff --git a/src/crypto/tls_openssl.h b/src/crypto/tls_openssl.h new file mode 100644 index 0000000000000..2a62d5c5d0198 --- /dev/null +++ b/src/crypto/tls_openssl.h @@ -0,0 +1,19 @@ +/* + * SSL/TLS interface functions for OpenSSL + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLS_OPENSSL_H +#define TLS_OPENSSL_H + +enum ocsp_result { + OCSP_GOOD, OCSP_REVOKED, OCSP_NO_RESPONSE, OCSP_INVALID +}; + +enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert, + X509 *issuer, X509 *issuer_issuer); + +#endif /* TLS_OPENSSL_H */ diff --git a/src/crypto/tls_openssl_ocsp.c b/src/crypto/tls_openssl_ocsp.c new file mode 100644 index 0000000000000..8b37b34e78901 --- /dev/null +++ b/src/crypto/tls_openssl_ocsp.c @@ -0,0 +1,846 @@ +/* + * SSL/TLS interface functions for OpenSSL - BoringSSL OCSP + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/x509v3.h> +#ifdef OPENSSL_IS_BORINGSSL +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#endif /* OPENSSL_IS_BORINGSSL */ + +#include "common.h" +#include "tls_openssl.h" + + +#ifdef OPENSSL_IS_BORINGSSL + +static void tls_show_errors(int level, const char *func, const char *txt) +{ + unsigned long err; + + wpa_printf(level, "OpenSSL: %s - %s %s", + func, txt, ERR_error_string(ERR_get_error(), NULL)); + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "OpenSSL: pending error: %s", + ERR_error_string(err, NULL)); + } +} + + +/* + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, -- Hash of Issuer's DN + * issuerKeyHash OCTET STRING, -- Hash of Issuer's public key + * serialNumber CertificateSerialNumber } + */ +typedef struct { + X509_ALGOR *hashAlgorithm; + ASN1_OCTET_STRING *issuerNameHash; + ASN1_OCTET_STRING *issuerKeyHash; + ASN1_INTEGER *serialNumber; +} CertID; + +/* + * ResponseBytes ::= SEQUENCE { + * responseType OBJECT IDENTIFIER, + * response OCTET STRING } + */ +typedef struct { + ASN1_OBJECT *responseType; + ASN1_OCTET_STRING *response; +} ResponseBytes; + +/* + * OCSPResponse ::= SEQUENCE { + * responseStatus OCSPResponseStatus, + * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + */ +typedef struct { + ASN1_ENUMERATED *responseStatus; + ResponseBytes *responseBytes; +} OCSPResponse; + +ASN1_SEQUENCE(ResponseBytes) = { + ASN1_SIMPLE(ResponseBytes, responseType, ASN1_OBJECT), + ASN1_SIMPLE(ResponseBytes, response, ASN1_OCTET_STRING) +} ASN1_SEQUENCE_END(ResponseBytes); + +ASN1_SEQUENCE(OCSPResponse) = { + ASN1_SIMPLE(OCSPResponse, responseStatus, ASN1_ENUMERATED), + ASN1_EXP_OPT(OCSPResponse, responseBytes, ResponseBytes, 0) +} ASN1_SEQUENCE_END(OCSPResponse); + +IMPLEMENT_ASN1_FUNCTIONS(OCSPResponse); + +/* + * ResponderID ::= CHOICE { + * byName [1] Name, + * byKey [2] KeyHash } + */ +typedef struct { + int type; + union { + X509_NAME *byName; + ASN1_OCTET_STRING *byKey; + } value; +} ResponderID; + +/* + * RevokedInfo ::= SEQUENCE { + * revocationTime GeneralizedTime, + * revocationReason [0] EXPLICIT CRLReason OPTIONAL } + */ +typedef struct { + ASN1_GENERALIZEDTIME *revocationTime; + ASN1_ENUMERATED *revocationReason; +} RevokedInfo; + +/* + * CertStatus ::= CHOICE { + * good [0] IMPLICIT NULL, + * revoked [1] IMPLICIT RevokedInfo, + * unknown [2] IMPLICIT UnknownInfo } + */ +typedef struct { + int type; + union { + ASN1_NULL *good; + RevokedInfo *revoked; + ASN1_NULL *unknown; + } value; +} CertStatus; + +/* + * SingleResponse ::= SEQUENCE { + * certID CertID, + * certStatus CertStatus, + * thisUpdate GeneralizedTime, + * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + * singleExtensions [1] EXPLICIT Extensions OPTIONAL } + */ +typedef struct { + CertID *certID; + CertStatus *certStatus; + ASN1_GENERALIZEDTIME *thisUpdate; + ASN1_GENERALIZEDTIME *nextUpdate; + STACK_OF(X509_EXTENSION) *singleExtensions; +} SingleResponse; + +/* + * ResponseData ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * responderID ResponderID, + * producedAt GeneralizedTime, + * responses SEQUENCE OF SingleResponse, + * responseExtensions [1] EXPLICIT Extensions OPTIONAL } + */ +typedef struct { + ASN1_INTEGER *version; + ResponderID *responderID; + ASN1_GENERALIZEDTIME *producedAt; + STACK_OF(SingleResponse) *responses; + STACK_OF(X509_EXTENSION) *responseExtensions; +} ResponseData; + +/* + * BasicOCSPResponse ::= SEQUENCE { + * tbsResponseData ResponseData, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + */ +typedef struct { + ResponseData *tbsResponseData; + X509_ALGOR *signatureAlgorithm; + ASN1_BIT_STRING *signature; + STACK_OF(X509) *certs; +} BasicOCSPResponse; + +ASN1_SEQUENCE(CertID) = { + ASN1_SIMPLE(CertID, hashAlgorithm, X509_ALGOR), + ASN1_SIMPLE(CertID, issuerNameHash, ASN1_OCTET_STRING), + ASN1_SIMPLE(CertID, issuerKeyHash, ASN1_OCTET_STRING), + ASN1_SIMPLE(CertID, serialNumber, ASN1_INTEGER) +} ASN1_SEQUENCE_END(CertID); + +ASN1_CHOICE(ResponderID) = { + ASN1_EXP(ResponderID, value.byName, X509_NAME, 1), + ASN1_EXP(ResponderID, value.byKey, ASN1_OCTET_STRING, 2) +} ASN1_CHOICE_END(ResponderID); + +ASN1_SEQUENCE(RevokedInfo) = { + ASN1_SIMPLE(RevokedInfo, revocationTime, ASN1_GENERALIZEDTIME), + ASN1_EXP_OPT(RevokedInfo, revocationReason, ASN1_ENUMERATED, 0) +} ASN1_SEQUENCE_END(RevokedInfo); + +ASN1_CHOICE(CertStatus) = { + ASN1_IMP(CertStatus, value.good, ASN1_NULL, 0), + ASN1_IMP(CertStatus, value.revoked, RevokedInfo, 1), + ASN1_IMP(CertStatus, value.unknown, ASN1_NULL, 2) +} ASN1_CHOICE_END(CertStatus); + +ASN1_SEQUENCE(SingleResponse) = { + ASN1_SIMPLE(SingleResponse, certID, CertID), + ASN1_SIMPLE(SingleResponse, certStatus, CertStatus), + ASN1_SIMPLE(SingleResponse, thisUpdate, ASN1_GENERALIZEDTIME), + ASN1_EXP_OPT(SingleResponse, nextUpdate, ASN1_GENERALIZEDTIME, 0), + ASN1_EXP_SEQUENCE_OF_OPT(SingleResponse, singleExtensions, + X509_EXTENSION, 1) +} ASN1_SEQUENCE_END(SingleResponse); + +ASN1_SEQUENCE(ResponseData) = { + ASN1_EXP_OPT(ResponseData, version, ASN1_INTEGER, 0), + ASN1_SIMPLE(ResponseData, responderID, ResponderID), + ASN1_SIMPLE(ResponseData, producedAt, ASN1_GENERALIZEDTIME), + ASN1_SEQUENCE_OF(ResponseData, responses, SingleResponse), + ASN1_EXP_SEQUENCE_OF_OPT(ResponseData, responseExtensions, + X509_EXTENSION, 1) +} ASN1_SEQUENCE_END(ResponseData); + +ASN1_SEQUENCE(BasicOCSPResponse) = { + ASN1_SIMPLE(BasicOCSPResponse, tbsResponseData, ResponseData), + ASN1_SIMPLE(BasicOCSPResponse, signatureAlgorithm, X509_ALGOR), + ASN1_SIMPLE(BasicOCSPResponse, signature, ASN1_BIT_STRING), + ASN1_EXP_SEQUENCE_OF_OPT(BasicOCSPResponse, certs, X509, 0) +} ASN1_SEQUENCE_END(BasicOCSPResponse); + +IMPLEMENT_ASN1_FUNCTIONS(BasicOCSPResponse); + +#define sk_SingleResponse_num(sk) \ +sk_num(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk)) + +#define sk_SingleResponse_value(sk, i) \ + ((SingleResponse *) \ + sk_value(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk), (i))) + + +static char * mem_bio_to_str(BIO *out) +{ + char *txt; + size_t rlen; + int res; + + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (!txt) { + BIO_free(out); + return NULL; + } + + res = BIO_read(out, txt, rlen); + BIO_free(out); + if (res < 0) { + os_free(txt); + return NULL; + } + + txt[res] = '\0'; + return txt; +} + + +static char * generalizedtime_str(ASN1_GENERALIZEDTIME *t) +{ + BIO *out; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + if (!ASN1_GENERALIZEDTIME_print(out, t)) { + BIO_free(out); + return NULL; + } + + return mem_bio_to_str(out); +} + + +static char * responderid_str(ResponderID *rid) +{ + BIO *out; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + switch (rid->type) { + case 0: + X509_NAME_print_ex(out, rid->value.byName, 0, XN_FLAG_ONELINE); + break; + case 1: + i2a_ASN1_STRING(out, rid->value.byKey, V_ASN1_OCTET_STRING); + break; + default: + BIO_free(out); + return NULL; + } + + return mem_bio_to_str(out); +} + + +static char * octet_string_str(ASN1_OCTET_STRING *o) +{ + BIO *out; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + i2a_ASN1_STRING(out, o, V_ASN1_OCTET_STRING); + return mem_bio_to_str(out); +} + + +static char * integer_str(ASN1_INTEGER *i) +{ + BIO *out; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + i2a_ASN1_INTEGER(out, i); + return mem_bio_to_str(out); +} + + +static char * algor_str(X509_ALGOR *alg) +{ + BIO *out; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + i2a_ASN1_OBJECT(out, alg->algorithm); + return mem_bio_to_str(out); +} + + +static char * extensions_str(const char *title, STACK_OF(X509_EXTENSION) *ext) +{ + BIO *out; + + if (!ext) + return NULL; + + out = BIO_new(BIO_s_mem()); + if (!out) + return NULL; + + if (!X509V3_extensions_print(out, title, ext, 0, 0)) { + BIO_free(out); + return NULL; + } + return mem_bio_to_str(out); +} + + +static int ocsp_resp_valid(ASN1_GENERALIZEDTIME *thisupd, + ASN1_GENERALIZEDTIME *nextupd) +{ + time_t now, tmp; + + if (!ASN1_GENERALIZEDTIME_check(thisupd)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Invalid OCSP response thisUpdate"); + return 0; + } + + time(&now); + tmp = now + 5 * 60; /* allow five minute clock difference */ + if (X509_cmp_time(thisupd, &tmp) > 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response not yet valid"); + return 0; + } + + if (!nextupd) + return 1; /* OK - no limit on response age */ + + if (!ASN1_GENERALIZEDTIME_check(nextupd)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Invalid OCSP response nextUpdate"); + return 0; + } + + tmp = now - 5 * 60; /* allow five minute clock difference */ + if (X509_cmp_time(nextupd, &tmp) < 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response expired"); + return 0; + } + + if (ASN1_STRING_cmp(nextupd, thisupd) < 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP response nextUpdate before thisUpdate"); + return 0; + } + + /* Both thisUpdate and nextUpdate are valid */ + return -1; +} + + +static int issuer_match(X509 *cert, X509 *issuer, CertID *certid) +{ + X509_NAME *iname; + ASN1_BIT_STRING *ikey; + const EVP_MD *dgst; + unsigned int len; + unsigned char md[EVP_MAX_MD_SIZE]; + ASN1_OCTET_STRING *hash; + char *txt; + + dgst = EVP_get_digestbyobj(certid->hashAlgorithm->algorithm); + if (!dgst) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not find matching hash algorithm for OCSP"); + return -1; + } + + iname = X509_get_issuer_name(cert); + if (!X509_NAME_digest(iname, dgst, md, &len)) + return -1; + hash = ASN1_OCTET_STRING_new(); + if (!hash) + return -1; + if (!ASN1_OCTET_STRING_set(hash, md, len)) { + ASN1_OCTET_STRING_free(hash); + return -1; + } + + txt = octet_string_str(hash); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerNameHash: %s", + txt); + os_free(txt); + } + + if (ASN1_OCTET_STRING_cmp(certid->issuerNameHash, hash)) { + ASN1_OCTET_STRING_free(hash); + return -1; + } + + ikey = X509_get0_pubkey_bitstr(issuer); + if (!ikey || + !EVP_Digest(ikey->data, ikey->length, md, &len, dgst, NULL) || + !ASN1_OCTET_STRING_set(hash, md, len)) { + ASN1_OCTET_STRING_free(hash); + return -1; + } + + txt = octet_string_str(hash); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerKeyHash: %s", + txt); + os_free(txt); + } + + if (ASN1_OCTET_STRING_cmp(certid->issuerKeyHash, hash)) { + ASN1_OCTET_STRING_free(hash); + return -1; + } + + ASN1_OCTET_STRING_free(hash); + return 0; +} + + +static X509 * ocsp_find_signer(STACK_OF(X509) *certs, ResponderID *rid) +{ + unsigned int i; + unsigned char hash[SHA_DIGEST_LENGTH]; + + if (rid->type == 0) { + /* byName */ + return X509_find_by_subject(certs, rid->value.byName); + } + + /* byKey */ + if (rid->value.byKey->length != SHA_DIGEST_LENGTH) + return NULL; + for (i = 0; i < sk_X509_num(certs); i++) { + X509 *x = sk_X509_value(certs, i); + + X509_pubkey_digest(x, EVP_sha1(), hash, NULL); + if (os_memcmp(rid->value.byKey->data, hash, + SHA_DIGEST_LENGTH) == 0) + return x; + } + + return NULL; +} + + +enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert, + X509 *issuer, X509 *issuer_issuer) +{ + const uint8_t *resp_data; + size_t resp_len; + OCSPResponse *resp; + int status; + ResponseBytes *bytes; + const u8 *basic_data; + size_t basic_len; + BasicOCSPResponse *basic; + ResponseData *rd; + char *txt; + int i, num; + unsigned int j, num_resp; + SingleResponse *matching_resp = NULL, *cmp_sresp; + enum ocsp_result result = OCSP_INVALID; + X509_STORE *store; + STACK_OF(X509) *untrusted = NULL, *certs = NULL, *chain = NULL; + X509_STORE_CTX ctx; + X509 *signer, *tmp_cert; + int signer_trusted = 0; + EVP_PKEY *skey; + int ret; + char buf[256]; + + txt = integer_str(X509_get_serialNumber(cert)); + if (txt) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Searching OCSP response for peer certificate serialNumber: %s", txt); + os_free(txt); + } + + SSL_get0_ocsp_response(ssl, &resp_data, &resp_len); + if (resp_data == NULL || resp_len == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received"); + return OCSP_NO_RESPONSE; + } + + wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", resp_data, resp_len); + + resp = d2i_OCSPResponse(NULL, &resp_data, resp_len); + if (!resp) { + wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSPResponse"); + return OCSP_INVALID; + } + + status = ASN1_ENUMERATED_get(resp->responseStatus); + if (status != 0) { + wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d", + status); + return OCSP_INVALID; + } + + bytes = resp->responseBytes; + + if (!bytes || + OBJ_obj2nid(bytes->responseType) != NID_id_pkix_OCSP_basic) { + wpa_printf(MSG_INFO, + "OpenSSL: Could not find BasicOCSPResponse"); + return OCSP_INVALID; + } + + basic_data = ASN1_STRING_data(bytes->response); + basic_len = ASN1_STRING_length(bytes->response); + wpa_hexdump(MSG_DEBUG, "OpenSSL: BasicOCSPResponse", + basic_data, basic_len); + + basic = d2i_BasicOCSPResponse(NULL, &basic_data, basic_len); + if (!basic) { + wpa_printf(MSG_INFO, + "OpenSSL: Could not parse BasicOCSPResponse"); + OCSPResponse_free(resp); + return OCSP_INVALID; + } + + rd = basic->tbsResponseData; + + if (basic->certs) { + untrusted = sk_X509_dup(basic->certs); + if (!untrusted) + goto fail; + + num = sk_X509_num(basic->certs); + for (i = 0; i < num; i++) { + X509 *extra_cert; + + extra_cert = sk_X509_value(basic->certs, i); + X509_NAME_oneline(X509_get_subject_name(extra_cert), + buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, + "OpenSSL: BasicOCSPResponse cert %s", buf); + + if (!sk_X509_push(untrusted, extra_cert)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not add certificate to the untrusted stack"); + } + } + } + + store = SSL_CTX_get_cert_store(ssl_ctx); + if (issuer) { + if (X509_STORE_add_cert(store, issuer) != 1) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: Could not add issuer to certificate store"); + } + certs = sk_X509_new_null(); + if (certs) { + tmp_cert = X509_dup(issuer); + if (tmp_cert && !sk_X509_push(certs, tmp_cert)) { + tls_show_errors( + MSG_INFO, __func__, + "OpenSSL: Could not add issuer to OCSP responder trust store"); + X509_free(tmp_cert); + sk_X509_free(certs); + certs = NULL; + } + if (certs && issuer_issuer) { + tmp_cert = X509_dup(issuer_issuer); + if (tmp_cert && + !sk_X509_push(certs, tmp_cert)) { + tls_show_errors( + MSG_INFO, __func__, + "OpenSSL: Could not add issuer's issuer to OCSP responder trust store"); + X509_free(tmp_cert); + } + } + } + } + + signer = ocsp_find_signer(certs, rd->responderID); + if (!signer) + signer = ocsp_find_signer(untrusted, rd->responderID); + else + signer_trusted = 1; + if (!signer) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not find OCSP signer certificate"); + goto fail; + } + + skey = X509_get_pubkey(signer); + if (!skey) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not get OCSP signer public key"); + goto fail; + } + if (ASN1_item_verify(ASN1_ITEM_rptr(ResponseData), + basic->signatureAlgorithm, basic->signature, + basic->tbsResponseData, skey) <= 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: BasicOCSPResponse signature is invalid"); + goto fail; + } + + X509_NAME_oneline(X509_get_subject_name(signer), buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, + "OpenSSL: Found OCSP signer certificate %s and verified BasicOCSPResponse signature", + buf); + + if (!X509_STORE_CTX_init(&ctx, store, signer, untrusted)) + goto fail; + X509_STORE_CTX_set_purpose(&ctx, X509_PURPOSE_OCSP_HELPER); + ret = X509_verify_cert(&ctx); + chain = X509_STORE_CTX_get1_chain(&ctx); + X509_STORE_CTX_cleanup(&ctx); + if (ret <= 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not validate OCSP signer certificate"); + goto fail; + } + + if (!chain || sk_X509_num(chain) <= 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP signer chain found"); + goto fail; + } + + if (!signer_trusted) { + X509_check_purpose(signer, -1, 0); + if ((signer->ex_flags & EXFLAG_XKUSAGE) && + (signer->ex_xkusage & XKU_OCSP_SIGN)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP signer certificate delegation OK"); + } else { + tmp_cert = sk_X509_value(chain, sk_X509_num(chain) - 1); + if (X509_check_trust(tmp_cert, NID_OCSP_sign, 0) != + X509_TRUST_TRUSTED) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP signer certificate not trusted"); + result = OCSP_NO_RESPONSE; + goto fail; + } + } + } + + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP version: %lu", + ASN1_INTEGER_get(rd->version)); + + txt = responderid_str(rd->responderID); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP responderID: %s", + txt); + os_free(txt); + } + + txt = generalizedtime_str(rd->producedAt); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP producedAt: %s", + txt); + os_free(txt); + } + + num_resp = sk_SingleResponse_num(rd->responses); + if (num_resp == 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: No OCSP SingleResponse within BasicOCSPResponse"); + result = OCSP_NO_RESPONSE; + goto fail; + } + cmp_sresp = sk_SingleResponse_value(rd->responses, 0); + for (j = 0; j < num_resp; j++) { + SingleResponse *sresp; + CertID *cid1, *cid2; + + sresp = sk_SingleResponse_value(rd->responses, j); + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP SingleResponse %u/%u", + j + 1, num_resp); + + txt = algor_str(sresp->certID->hashAlgorithm); + if (txt) { + wpa_printf(MSG_DEBUG, + "OpenSSL: certID hashAlgorithm: %s", txt); + os_free(txt); + } + + txt = octet_string_str(sresp->certID->issuerNameHash); + if (txt) { + wpa_printf(MSG_DEBUG, + "OpenSSL: certID issuerNameHash: %s", txt); + os_free(txt); + } + + txt = octet_string_str(sresp->certID->issuerKeyHash); + if (txt) { + wpa_printf(MSG_DEBUG, + "OpenSSL: certID issuerKeyHash: %s", txt); + os_free(txt); + } + + txt = integer_str(sresp->certID->serialNumber); + if (txt) { + wpa_printf(MSG_DEBUG, + "OpenSSL: certID serialNumber: %s", txt); + os_free(txt); + } + + switch (sresp->certStatus->type) { + case 0: + wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: good"); + break; + case 1: + wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: revoked"); + break; + default: + wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: unknown"); + break; + } + + txt = generalizedtime_str(sresp->thisUpdate); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: thisUpdate: %s", txt); + os_free(txt); + } + + if (sresp->nextUpdate) { + txt = generalizedtime_str(sresp->nextUpdate); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: nextUpdate: %s", + txt); + os_free(txt); + } + } + + txt = extensions_str("singleExtensions", + sresp->singleExtensions); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt); + os_free(txt); + } + + cid1 = cmp_sresp->certID; + cid2 = sresp->certID; + if (j > 0 && + (OBJ_cmp(cid1->hashAlgorithm->algorithm, + cid2->hashAlgorithm->algorithm) != 0 || + ASN1_OCTET_STRING_cmp(cid1->issuerNameHash, + cid2->issuerNameHash) != 0 || + ASN1_OCTET_STRING_cmp(cid1->issuerKeyHash, + cid2->issuerKeyHash) != 0)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Different OCSP response issuer information between SingleResponse values within BasicOCSPResponse"); + goto fail; + } + + if (!matching_resp && issuer && + ASN1_INTEGER_cmp(sresp->certID->serialNumber, + X509_get_serialNumber(cert)) == 0 && + issuer_match(cert, issuer, sresp->certID) == 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: This response matches peer certificate"); + matching_resp = sresp; + } + } + + txt = extensions_str("responseExtensions", rd->responseExtensions); + if (txt) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt); + os_free(txt); + } + + if (!matching_resp) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not find OCSP response that matches the peer certificate"); + result = OCSP_NO_RESPONSE; + goto fail; + } + + if (!ocsp_resp_valid(matching_resp->thisUpdate, + matching_resp->nextUpdate)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP response not valid at this time"); + goto fail; + } + + if (matching_resp->certStatus->type == 1) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP response indicated that the peer certificate has been revoked"); + result = OCSP_REVOKED; + goto fail; + } + + if (matching_resp->certStatus->type != 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: OCSP response did not indicate good status"); + result = OCSP_NO_RESPONSE; + goto fail; + } + + /* OCSP response indicated the certificate is good. */ + result = OCSP_GOOD; +fail: + sk_X509_pop_free(chain, X509_free); + sk_X509_free(untrusted); + sk_X509_pop_free(certs, X509_free); + BasicOCSPResponse_free(basic); + OCSPResponse_free(resp); + + return result; +} + +#endif /* OPENSSL_IS_BORINGSSL */ diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 3cdab5a7a87d6..a449cc9347352 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -45,6 +45,22 @@ #define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000 #define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000 +#define HOSTAPD_CHAN_VHT_10_150 0x00100000 +#define HOSTAPD_CHAN_VHT_30_130 0x00200000 +#define HOSTAPD_CHAN_VHT_50_110 0x00400000 +#define HOSTAPD_CHAN_VHT_70_90 0x00800000 +#define HOSTAPD_CHAN_VHT_90_70 0x01000000 +#define HOSTAPD_CHAN_VHT_110_50 0x02000000 +#define HOSTAPD_CHAN_VHT_130_30 0x04000000 +#define HOSTAPD_CHAN_VHT_150_10 0x08000000 + +/* Filter gratuitous ARP */ +#define WPA_DATA_FRAME_FILTER_FLAG_ARP BIT(0) +/* Filter unsolicited Neighbor Advertisement */ +#define WPA_DATA_FRAME_FILTER_FLAG_NA BIT(1) +/* Filter unicast IP packets encrypted using the GTK */ +#define WPA_DATA_FRAME_FILTER_FLAG_GTK BIT(2) + /** * enum reg_change_initiator - Regulatory change initiator */ @@ -284,6 +300,18 @@ struct wpa_interface_info { #define WPAS_MAX_SCAN_SSIDS 16 /** + * struct wpa_driver_scan_ssid - SSIDs to scan for + * @ssid - specific SSID to scan for (ProbeReq) + * %NULL or zero-length SSID is used to indicate active scan + * with wildcard SSID. + * @ssid_len - Length of the SSID in octets + */ +struct wpa_driver_scan_ssid { + const u8 *ssid; + size_t ssid_len; +}; + +/** * struct wpa_driver_scan_params - Scan parameters * Data for struct wpa_driver_ops::scan2(). */ @@ -291,18 +319,7 @@ struct wpa_driver_scan_params { /** * ssids - SSIDs to scan for */ - struct wpa_driver_scan_ssid { - /** - * ssid - specific SSID to scan for (ProbeReq) - * %NULL or zero-length SSID is used to indicate active scan - * with wildcard SSID. - */ - const u8 *ssid; - /** - * ssid_len: Length of the SSID in octets - */ - size_t ssid_len; - } ssids[WPAS_MAX_SCAN_SSIDS]; + struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS]; /** * num_ssids - Number of entries in ssids array @@ -407,6 +424,37 @@ struct wpa_driver_scan_params { */ const u8 *mac_addr_mask; + /** + * sched_scan_plans - Scan plans for scheduled scan + * + * Each scan plan consists of the number of iterations to scan and the + * interval between scans. When a scan plan finishes (i.e., it was run + * for the specified number of iterations), the next scan plan is + * executed. The scan plans are executed in the order they appear in + * the array (lower index first). The last scan plan will run infinitely + * (until requested to stop), thus must not specify the number of + * iterations. All other scan plans must specify the number of + * iterations. + */ + struct sched_scan_plan { + u32 interval; /* In seconds */ + u32 iterations; /* Zero to run infinitely */ + } *sched_scan_plans; + + /** + * sched_scan_plans_num - Number of scan plans in sched_scan_plans array + */ + unsigned int sched_scan_plans_num; + + /** + * bssid - Specific BSSID to scan for + * + * This optional parameter can be used to replace the default wildcard + * BSSID with a specific BSSID to scan for if results are needed from + * only a single BSS. + */ + const u8 *bssid; + /* * NOTE: Whenever adding new parameters here, please make sure * wpa_scan_clone_params() and wpa_scan_free_params() get updated with @@ -828,6 +876,12 @@ struct wpa_driver_associate_params { * RRM (Radio Resource Measurements) */ int rrm_used; + + /** + * pbss - If set, connect to a PCP in a PBSS. Otherwise, connect to an + * AP as usual. Valid for DMG network only. + */ + int pbss; }; enum hide_ssid { @@ -1055,16 +1109,28 @@ struct wpa_driver_ap_params { * reenable - Whether this is to re-enable beaconing */ int reenable; + + /** + * pbss - Whether to start a PCP (in PBSS) instead of an AP in + * infrastructure BSS. Valid only for DMG network. + */ + int pbss; }; struct wpa_driver_mesh_bss_params { -#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS 0x00000001 +#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS 0x00000001 +#define WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT 0x00000002 +#define WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS 0x00000004 +#define WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE 0x00000008 /* * TODO: Other mesh configuration parameters would go here. * See NL80211_MESHCONF_* for all the mesh config parameters. */ unsigned int flags; + int auto_plinks; int peer_link_timeout; + int max_peer_links; + u16 ht_opmode; }; struct wpa_driver_mesh_join_params { @@ -1075,7 +1141,7 @@ struct wpa_driver_mesh_join_params { int ie_len; struct hostapd_freq_params freq; int beacon_int; - int max_peer_links; + int dtim_period; struct wpa_driver_mesh_bss_params conf; #define WPA_DRIVER_MESH_FLAG_USER_MPM 0x00000001 #define WPA_DRIVER_MESH_FLAG_DRIVER_MPM 0x00000002 @@ -1214,8 +1280,17 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_VHT_IBSS 0x0000002000000000ULL /** Driver supports automatic band selection */ #define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY 0x0000004000000000ULL +/** Driver supports simultaneous off-channel operations */ +#define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS 0x0000008000000000ULL +/** Driver supports full AP client state */ +#define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE 0x0000010000000000ULL +/** Driver supports P2P Listen offload */ +#define WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD 0x0000020000000000ULL u64 flags; +#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \ + (drv_flags & WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE) + #define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001 #define WPA_DRIVER_SMPS_MODE_DYNAMIC 0x00000002 unsigned int smps_modes; @@ -1231,6 +1306,15 @@ struct wpa_driver_capa { /** Maximum number of supported active probe SSIDs for sched_scan */ int max_sched_scan_ssids; + /** Maximum number of supported scan plans for scheduled scan */ + unsigned int max_sched_scan_plans; + + /** Maximum interval in a scan plan. In seconds */ + u32 max_sched_scan_plan_interval; + + /** Maximum number of iterations in a single scan plan */ + u32 max_sched_scan_plan_iterations; + /** Whether sched_scan (offloaded scanning) is supported */ int sched_scan_supported; @@ -1296,6 +1380,12 @@ struct wpa_driver_capa { * offset, namely the 6th byte in the Action frame body. */ #define WPA_DRIVER_FLAGS_TX_POWER_INSERTION 0x00000008 +/** + * Driver supports RRM. With this support, the driver will accept to use RRM in + * (Re)Association Request frames, without supporting quiet period. + */ +#define WPA_DRIVER_FLAGS_SUPPORT_RRM 0x00000010 + u32 rrm_flags; /* Driver concurrency capabilities */ @@ -1304,13 +1394,18 @@ struct wpa_driver_capa { unsigned int max_conc_chan_2_4; /* Maximum number of concurrent channels on 5 GHz */ unsigned int max_conc_chan_5_0; + + /* Maximum number of supported CSA counters */ + u16 max_csa_counters; }; struct hostapd_data; struct hostap_sta_driver_data { - unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes; + unsigned long rx_packets, tx_packets; + unsigned long long rx_bytes, tx_bytes; + int bytes_64bit; /* whether 64-bit byte counters are supported */ unsigned long current_tx_rate; unsigned long inactive_msec; unsigned long flags; @@ -1336,6 +1431,7 @@ struct hostapd_sta_add_params { u32 flags_mask; /* unset bits in flags */ #ifdef CONFIG_MESH enum mesh_plink_state plink_state; + u16 peer_aid; #endif /* CONFIG_MESH */ int set; /* Set STA parameters instead of add */ u8 qosinfo; @@ -1345,6 +1441,7 @@ struct hostapd_sta_add_params { size_t supp_channels_len; const u8 *supp_oper_classes; size_t supp_oper_classes_len; + int support_p2p_ps; }; struct mac_address { @@ -1450,6 +1547,7 @@ struct wpa_bss_params { #define WPA_STA_MFP BIT(3) #define WPA_STA_TDLS_PEER BIT(4) #define WPA_STA_AUTHENTICATED BIT(5) +#define WPA_STA_ASSOCIATED BIT(6) enum tdls_oper { TDLS_DISCOVERY_REQ, @@ -1554,8 +1652,8 @@ struct csa_settings { struct beacon_data beacon_csa; struct beacon_data beacon_after; - u16 counter_offset_beacon; - u16 counter_offset_presp; + u16 counter_offset_beacon[2]; + u16 counter_offset_presp[2]; }; /* TDLS peer capabilities for send_tdls_mgmt() */ @@ -1883,6 +1981,14 @@ struct wpa_driver_ops { void (*poll)(void *priv); /** + * get_ifindex - Get interface index + * @priv: private driver interface data + * + * Returns: Interface index + */ + unsigned int (*get_ifindex)(void *priv); + + /** * get_ifname - Get interface name * @priv: private driver interface data * @@ -1960,10 +2066,13 @@ struct wpa_driver_ops { * @noack: Do not wait for this frame to be acked (disable retries) * @freq: Frequency (in MHz) to send the frame on, or 0 to let the * driver decide + * @csa_offs: Array of CSA offsets or %NULL + * @csa_offs_len: Number of elements in csa_offs * Returns: 0 on success, -1 on failure */ int (*send_mlme)(void *priv, const u8 *data, size_t data_len, - int noack, unsigned int freq); + int noack, unsigned int freq, const u16 *csa_offs, + size_t csa_offs_len); /** * update_ft_ies - Update FT (IEEE 802.11r) IEs @@ -2013,6 +2122,7 @@ struct wpa_driver_ops { /** * global_init - Global driver initialization + * @ctx: wpa_global pointer * Returns: Pointer to private data (global), %NULL on failure * * This optional function is called to initialize the driver wrapper @@ -2022,7 +2132,7 @@ struct wpa_driver_ops { * use init2() function instead of init() to get the pointer to global * data available to per-interface initializer. */ - void * (*global_init)(void); + void * (*global_init)(void *ctx); /** * global_deinit - Global driver deinitialization @@ -2308,12 +2418,17 @@ struct wpa_driver_ops { * @params: Station parameters * Returns: 0 on success, -1 on failure * - * This function is used to add a station entry to the driver once the - * station has completed association. This is only used if the driver + * This function is used to add or set (params->set 1) a station + * entry in the driver. Adding STA entries is used only if the driver * does not take care of association processing. * - * With TDLS, this function is also used to add or set (params->set 1) - * TDLS peer entries. + * With drivers that don't support full AP client state, this function + * is used to add a station entry to the driver once the station has + * completed association. + * + * With TDLS, this function is used to add or set (params->set 1) + * TDLS peer entries (even with drivers that do not support full AP + * client state). */ int (*sta_add)(void *priv, struct hostapd_sta_add_params *params); @@ -2399,12 +2514,13 @@ struct wpa_driver_ops { * change interface address) * @bridge: Bridge interface to use or %NULL if no bridge configured * @use_existing: Whether to allow existing interface to be used + * @setup_ap: Whether to setup AP for %WPA_IF_AP_BSS interfaces * Returns: 0 on success, -1 on failure */ int (*if_add)(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, - const char *bridge, int use_existing); + const char *bridge, int use_existing, int setup_ap); /** * if_remove - Remove a virtual interface @@ -2986,7 +3102,6 @@ struct wpa_driver_ops { * sched_scan - Request the driver to initiate scheduled scan * @priv: Private driver interface data * @params: Scan parameters - * @interval: Interval between scan cycles in milliseconds * Returns: 0 on success, -1 on failure * * This operation should be used for scheduled scan offload to @@ -2997,8 +3112,7 @@ struct wpa_driver_ops { * and if not provided or if it returns -1, we fall back to * normal host-scheduled scans. */ - int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params, - u32 interval); + int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params); /** * stop_sched_scan - Request the driver to stop a scheduled scan @@ -3203,11 +3317,9 @@ struct wpa_driver_ops { * set_current_cipher_suite - Set current cipher suite * @priv: Private driver interface data * @cs: EUI64 identifier - * @cs_len: Length of the cs buffer in octets * Returns: 0 on success, -1 on failure (or if not supported) */ - int (*set_current_cipher_suite)(void *priv, const u8 *cs, - size_t cs_len); + int (*set_current_cipher_suite)(void *priv, u64 cs); /** * enable_controlled_port - Set controlled port status @@ -3439,6 +3551,78 @@ struct wpa_driver_ops { * on. Local device is assuming P2P Client role. */ int (*set_prob_oper_freq)(void *priv, unsigned int freq); + + /** + * abort_scan - Request the driver to abort an ongoing scan + * @priv: Private driver interface data + * Returns 0 on success, -1 on failure + */ + int (*abort_scan)(void *priv); + + /** + * configure_data_frame_filters - Request to configure frame filters + * @priv: Private driver interface data + * @filter_flags: The type of frames to filter (bitfield of + * WPA_DATA_FRAME_FILTER_FLAG_*) + * Returns: 0 on success or -1 on failure + */ + int (*configure_data_frame_filters)(void *priv, u32 filter_flags); + + /** + * get_ext_capab - Get extended capabilities for the specified interface + * @priv: Private driver interface data + * @type: Interface type for which to get extended capabilities + * @ext_capab: Extended capabilities fetched + * @ext_capab_mask: Extended capabilities mask + * @ext_capab_len: Length of the extended capabilities + * Returns: 0 on success or -1 on failure + */ + int (*get_ext_capab)(void *priv, enum wpa_driver_if_type type, + const u8 **ext_capab, const u8 **ext_capab_mask, + unsigned int *ext_capab_len); + + /** + * p2p_lo_start - Start offloading P2P listen to device + * @priv: Private driver interface data + * @freq: Listening frequency (MHz) for P2P listen + * @period: Length of the listen operation in milliseconds + * @interval: Interval for running the listen operation in milliseconds + * @count: Number of times to run the listen operation + * @device_types: Device primary and secondary types + * @dev_types_len: Number of bytes for device_types + * @ies: P2P IE and WSC IE for Probe Response frames + * @ies_len: Length of ies in bytes + * Returns: 0 on success or -1 on failure + */ + int (*p2p_lo_start)(void *priv, unsigned int freq, + unsigned int period, unsigned int interval, + unsigned int count, + const u8 *device_types, size_t dev_types_len, + const u8 *ies, size_t ies_len); + + /** + * p2p_lo_stop - Stop P2P listen offload + * @priv: Private driver interface data + * Returns: 0 on success or -1 on failure + */ + int (*p2p_lo_stop)(void *priv); + + /** + * set_default_scan_ies - Set default scan IEs + * @priv: Private driver interface data + * @ies: Scan default IEs buffer + * @ies_len: Length of IEs in bytes + * Returns: 0 on success or -1 on failure + * + * The driver can use these by default when there are no scan IEs coming + * in the subsequent scan requests. Also in case of one or more of IEs + * given in set_default_scan_ies() are missing in the subsequent scan + * request, the driver should merge the missing scan IEs in the scan + * request from the IEs set by set_default_scan_ies() in the Probe + * Request frames sent. + */ + int (*set_default_scan_ies)(void *priv, const u8 *ies, size_t ies_len); + }; @@ -3923,6 +4107,11 @@ enum wpa_event_type { * on a DFS frequency by a driver that supports DFS Offload. */ EVENT_DFS_CAC_STARTED, + + /** + * EVENT_P2P_LO_STOP - Notify that P2P listen offload is stopped + */ + EVENT_P2P_LO_STOP, }; @@ -4098,6 +4287,12 @@ union wpa_event_data { * ptk_kek_len - The length of ptk_kek */ size_t ptk_kek_len; + + /** + * subnet_status - The subnet status: + * 0 = unknown, 1 = unchanged, 2 = changed + */ + u8 subnet_status; } assoc_info; /** @@ -4174,6 +4369,7 @@ union wpa_event_data { * struct interface_status - Data for EVENT_INTERFACE_STATUS */ struct interface_status { + unsigned int ifindex; char ifname[100]; enum { EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED @@ -4301,6 +4497,12 @@ union wpa_event_data { * status_code - Status Code from (Re)association Response */ u16 status_code; + + /** + * timed_out - Whether failure is due to timeout (etc.) rather + * than explicit rejection response from the AP. + */ + int timed_out; } assoc_reject; struct timeout_event { @@ -4381,6 +4583,9 @@ union wpa_event_data { * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard * SSID) * @num_ssids: Number of entries in ssids array + * @external_scan: Whether the scan info is for an external scan + * @nl_scan_event: 1 if the source of this scan event is a normal scan, + * 0 if the source of the scan event is a vendor scan */ struct scan_info { int aborted; @@ -4388,6 +4593,8 @@ union wpa_event_data { size_t num_freqs; struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS]; size_t num_ssids; + int external_scan; + int nl_scan_event; } scan_info; /** @@ -4630,6 +4837,27 @@ union wpa_event_data { u16 ch_width; enum hostapd_hw_mode hw_mode; } acs_selected_channels; + + /** + * struct p2p_lo_stop - Reason code for P2P Listen offload stop event + * @reason_code: Reason for stopping offload + * P2P_LO_STOPPED_REASON_COMPLETE: Listen offload finished as + * scheduled. + * P2P_LO_STOPPED_REASON_RECV_STOP_CMD: Host requested offload to + * be stopped. + * P2P_LO_STOPPED_REASON_INVALID_PARAM: Invalid listen offload + * parameters. + * P2P_LO_STOPPED_REASON_NOT_SUPPORTED: Listen offload not + * supported by device. + */ + struct p2p_lo_stop { + enum { + P2P_LO_STOPPED_REASON_COMPLETE = 0, + P2P_LO_STOPPED_REASON_RECV_STOP_CMD, + P2P_LO_STOPPED_REASON_INVALID_PARAM, + P2P_LO_STOPPED_REASON_NOT_SUPPORTED, + } reason_code; + } p2p_lo_stop; }; /** @@ -4645,6 +4873,18 @@ union wpa_event_data { void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data); +/** + * wpa_supplicant_event_global - Report a driver event for wpa_supplicant + * @ctx: Context pointer (wpa_s); this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @event: event type (defined above) + * @data: possible extra data for the event + * + * Same as wpa_supplicant_event(), but we search for the interface in + * wpa_global. + */ +void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event, + union wpa_event_data *data); /* * The following inline functions are provided for convenience to simplify @@ -4697,8 +4937,52 @@ int vht_supported(const struct hostapd_hw_modes *mode); struct wowlan_triggers * wpa_get_wowlan_triggers(const char *wowlan_triggers, const struct wpa_driver_capa *capa); +/* Convert driver flag to string */ +const char * driver_flag_to_string(u64 flag); /* NULL terminated array of linked in driver wrappers */ extern const struct wpa_driver_ops *const wpa_drivers[]; + +/* Available drivers */ + +#ifdef CONFIG_DRIVER_WEXT +extern const struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */ +#endif /* CONFIG_DRIVER_WEXT */ +#ifdef CONFIG_DRIVER_NL80211 +/* driver_nl80211.c */ +extern const struct wpa_driver_ops wpa_driver_nl80211_ops; +#endif /* CONFIG_DRIVER_NL80211 */ +#ifdef CONFIG_DRIVER_HOSTAP +extern const struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ +#endif /* CONFIG_DRIVER_HOSTAP */ +#ifdef CONFIG_DRIVER_BSD +extern const struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ +#endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_OPENBSD +/* driver_openbsd.c */ +extern const struct wpa_driver_ops wpa_driver_openbsd_ops; +#endif /* CONFIG_DRIVER_OPENBSD */ +#ifdef CONFIG_DRIVER_NDIS +extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */ +#endif /* CONFIG_DRIVER_NDIS */ +#ifdef CONFIG_DRIVER_WIRED +extern const struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ +#endif /* CONFIG_DRIVER_WIRED */ +#ifdef CONFIG_DRIVER_MACSEC_QCA +/* driver_macsec_qca.c */ +extern const struct wpa_driver_ops wpa_driver_macsec_qca_ops; +#endif /* CONFIG_DRIVER_MACSEC_QCA */ +#ifdef CONFIG_DRIVER_ROBOSWITCH +/* driver_roboswitch.c */ +extern const struct wpa_driver_ops wpa_driver_roboswitch_ops; +#endif /* CONFIG_DRIVER_ROBOSWITCH */ +#ifdef CONFIG_DRIVER_ATHEROS +/* driver_atheros.c */ +extern const struct wpa_driver_ops wpa_driver_atheros_ops; +#endif /* CONFIG_DRIVER_ATHEROS */ +#ifdef CONFIG_DRIVER_NONE +extern const struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ +#endif /* CONFIG_DRIVER_NONE */ + #endif /* DRIVER_H */ diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index ef140934973af..a88345fc92f8f 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -189,13 +189,13 @@ set80211priv(struct atheros_driver_data *drv, int op, void *data, int len) op == IEEE80211_IOCTL_FILTERFRAME) do_inline = 0; - memset(&iwr, 0, sizeof(iwr)); + os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); if (do_inline) { /* * Argument data fits inline; put it there. */ - memcpy(iwr.u.name, data, len); + os_memcpy(iwr.u.name, data, len); } else { /* * Argument data too big for inline transfer; setup a @@ -222,10 +222,10 @@ set80211param(struct atheros_driver_data *drv, int op, int arg) { struct iwreq iwr; - memset(&iwr, 0, sizeof(iwr)); + os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.mode = op; - memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg)); + os_memcpy(iwr.u.name + sizeof(__u32), &arg, sizeof(arg)); if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { wpa_printf(MSG_INFO, @@ -244,9 +244,9 @@ ether_sprintf(const u8 *addr) static char buf[sizeof(MACSTR)]; if (addr != NULL) - snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); else - snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); + os_snprintf(buf, sizeof(buf), MACSTR, 0, 0, 0, 0, 0, 0); return buf; } #endif /* CONFIG_NO_STDOUT_DEBUG */ @@ -422,7 +422,7 @@ atheros_set_sta_authorized(void *priv, const u8 *addr, int authorized) else mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; mlme.im_reason = 0; - memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR, @@ -455,9 +455,9 @@ atheros_del_key(void *priv, const u8 *addr, int key_idx) wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d", __func__, ether_sprintf(addr), key_idx); - memset(&wk, 0, sizeof(wk)); + os_memset(&wk, 0, sizeof(wk)); if (addr != NULL) { - memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE; } else { wk.idk_keyix = key_idx; @@ -538,20 +538,20 @@ atheros_set_key(const char *ifname, void *priv, enum wpa_alg alg, return -3; } - memset(&wk, 0, sizeof(wk)); + os_memset(&wk, 0, sizeof(wk)); wk.ik_type = cipher; wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; if (addr == NULL || is_broadcast_ether_addr(addr)) { - memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); wk.ik_keyix = key_idx; if (set_tx) wk.ik_flags |= IEEE80211_KEY_DEFAULT; } else { - memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); wk.ik_keyix = IEEE80211_KEYIX_NONE; } wk.ik_keylen = key_len; - memcpy(wk.ik_keydata, key, key_len); + os_memcpy(wk.ik_keydata, key, key_len); ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk)); if (ret < 0) { @@ -575,11 +575,11 @@ atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", __func__, ether_sprintf(addr), idx); - memset(&wk, 0, sizeof(wk)); + os_memset(&wk, 0, sizeof(wk)); if (addr == NULL) - memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); else - memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); wk.ik_keyix = idx; if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) { @@ -600,13 +600,13 @@ atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, #define WPA_KEY_RSC_LEN 8 #endif u8 tmp[WPA_KEY_RSC_LEN]; - memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); + os_memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); for (i = 0; i < WPA_KEY_RSC_LEN; i++) { seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; } } #else /* WORDS_BIGENDIAN */ - memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); + os_memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); #endif /* WORDS_BIGENDIAN */ return 0; } @@ -616,7 +616,7 @@ static int atheros_flush(void *priv) { u8 allsta[IEEE80211_ADDR_LEN]; - memset(allsta, 0xff, IEEE80211_ADDR_LEN); + os_memset(allsta, 0xff, IEEE80211_ADDR_LEN); return atheros_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE); } @@ -629,19 +629,19 @@ atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, struct atheros_driver_data *drv = priv; struct ieee80211req_sta_stats stats; - memset(data, 0, sizeof(*data)); + os_memset(data, 0, sizeof(*data)); /* * Fetch statistics for station from the system. */ - memset(&stats, 0, sizeof(stats)); - memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); + os_memset(&stats, 0, sizeof(stats)); + os_memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); if (set80211priv(drv, IEEE80211_IOCTL_STA_STATS, &stats, sizeof(stats))) { wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr " MACSTR ")", __func__, MAC2STR(addr)); - if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { - memcpy(data, &drv->acct_data, sizeof(*data)); + if (os_memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + os_memcpy(data, &drv->acct_data, sizeof(*data)); return 0; } @@ -668,7 +668,7 @@ atheros_sta_clear_stats(void *priv, const u8 *addr) wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr)); mlme.im_op = IEEE80211_MLME_CLEAR_STATS; - memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); if (ret < 0) { @@ -744,7 +744,7 @@ atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, mlme.im_op = IEEE80211_MLME_DEAUTH; mlme.im_reason = reason_code; - memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR @@ -768,7 +768,7 @@ atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, mlme.im_op = IEEE80211_MLME_DISASSOC; mlme.im_reason = reason_code; - memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr " @@ -794,7 +794,7 @@ static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set, wpa_printf(MSG_ERROR, "Invalid QoS Map"); return -1; } else { - memset(&req, 0, sizeof(struct ieee80211req_athdbg)); + os_memset(&req, 0, sizeof(struct ieee80211req_athdbg)); req.cmd = IEEE80211_DBGREQ_SETQOSMAPCONF; os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, sizeof(iwr.ifr_name)); @@ -855,20 +855,29 @@ static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, (int) len); if (stype == WLAN_FC_STYPE_PROBE_REQ) { - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + if (len < IEEE80211_HDRLEN) return; os_memset(&event, 0, sizeof(event)); event.rx_probe_req.sa = mgmt->sa; event.rx_probe_req.da = mgmt->da; event.rx_probe_req.bssid = mgmt->bssid; - event.rx_probe_req.ie = mgmt->u.probe_req.variable; - event.rx_probe_req.ie_len = - len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + event.rx_probe_req.ie = buf + IEEE80211_HDRLEN; + event.rx_probe_req.ie_len = len - IEEE80211_HDRLEN; wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event); return; } + if (stype == WLAN_FC_STYPE_ACTION && + (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) == 0 || + is_broadcast_ether_addr(mgmt->bssid))) { + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); + return; + } + if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", __func__); @@ -890,12 +899,6 @@ static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, iebuf = mgmt->u.reassoc_req.variable; drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1); break; - case WLAN_FC_STYPE_ACTION: - os_memset(&event, 0, sizeof(event)); - event.rx_mgmt.frame = buf; - event.rx_mgmt.frame_len = len; - wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); - break; case WLAN_FC_STYPE_AUTH: if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) break; @@ -1120,8 +1123,8 @@ atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) /* * Fetch negotiated WPA/RSN parameters from the system. */ - memset(&ie, 0, sizeof(ie)); - memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); + os_memset(&ie, 0, sizeof(ie)); + os_memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { /* * See ATH_WPS_IE comment in the beginning of the file for a @@ -1171,10 +1174,10 @@ atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) no_ie: drv_event_assoc(hapd, addr, iebuf, ielen, 0); - if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + if (os_memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { /* Cached accounting data is not valid anymore. */ - memset(drv->acct_mac, 0, ETH_ALEN); - memset(&drv->acct_data, 0, sizeof(drv->acct_data)); + os_memset(drv->acct_mac, 0, ETH_ALEN); + os_memset(&drv->acct_data, 0, sizeof(drv->acct_data)); } } @@ -1185,10 +1188,10 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, #define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */ wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); - if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { char *pos; u8 addr[ETH_ALEN]; - pos = strstr(custom, "addr="); + pos = os_strstr(custom, "addr="); if (pos == NULL) { wpa_printf(MSG_DEBUG, "MLME-MICHAELMICFAILURE.indication " @@ -1212,33 +1215,33 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, char *key, *value; u32 val; key = custom; - while ((key = strchr(key, '\n')) != NULL) { + while ((key = os_strchr(key, '\n')) != NULL) { key++; - value = strchr(key, '='); + value = os_strchr(key, '='); if (value == NULL) continue; *value++ = '\0'; val = strtoul(value, NULL, 10); - if (strcmp(key, "mac") == 0) + if (os_strcmp(key, "mac") == 0) hwaddr_aton(value, drv->acct_mac); - else if (strcmp(key, "rx_packets") == 0) + else if (os_strcmp(key, "rx_packets") == 0) drv->acct_data.rx_packets = val; - else if (strcmp(key, "tx_packets") == 0) + else if (os_strcmp(key, "tx_packets") == 0) drv->acct_data.tx_packets = val; - else if (strcmp(key, "rx_bytes") == 0) + else if (os_strcmp(key, "rx_bytes") == 0) drv->acct_data.rx_bytes = val; - else if (strcmp(key, "tx_bytes") == 0) + else if (os_strcmp(key, "tx_bytes") == 0) drv->acct_data.tx_bytes = val; key = value; } #ifdef CONFIG_WPS - } else if (strncmp(custom, "PUSH-BUTTON.indication", 22) == 0) { + } else if (os_strncmp(custom, "PUSH-BUTTON.indication", 22) == 0) { /* Some atheros kernels send push button as a wireless event */ /* PROBLEM! this event is received for ALL BSSs ... * so all are enabled for WPS... ugh. */ wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL); - } else if (strncmp(custom, "Manage.prob_req ", 16) == 0) { + } else if (os_strncmp(custom, "Manage.prob_req ", 16) == 0) { /* * Atheros driver uses a hack to pass Probe Request frames as a * binary data in the custom wireless event. The old way (using @@ -1246,7 +1249,7 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, * Format: "Manage.prob_req <frame len>" | zero padding | frame */ int len = atoi(custom + 16); - if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { + if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) { wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event " "length %d", len); return; @@ -1255,11 +1258,11 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); #endif /* CONFIG_WPS */ #if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) - } else if (strncmp(custom, "Manage.assoc_req ", 17) == 0) { + } else if (os_strncmp(custom, "Manage.assoc_req ", 17) == 0) { /* Format: "Manage.assoc_req <frame len>" | zero padding | * frame */ int len = atoi(custom + 17); - if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { + if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) { wpa_printf(MSG_DEBUG, "Invalid Manage.assoc_req event length %d", len); @@ -1267,11 +1270,11 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, } atheros_raw_receive(drv, NULL, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); - } else if (strncmp(custom, "Manage.auth ", 12) == 0) { + } else if (os_strncmp(custom, "Manage.auth ", 12) == 0) { /* Format: "Manage.auth <frame len>" | zero padding | frame */ int len = atoi(custom + 12); if (len < 0 || - custom + MGMT_FRAM_TAG_SIZE + len > end) { + MGMT_FRAM_TAG_SIZE + len > end - custom) { wpa_printf(MSG_DEBUG, "Invalid Manage.auth event length %d", len); return; @@ -1280,11 +1283,11 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); #endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */ #ifdef ATHEROS_USE_RAW_RECEIVE - } else if (strncmp(custom, "Manage.action ", 14) == 0) { + } else if (os_strncmp(custom, "Manage.action ", 14) == 0) { /* Format: "Manage.assoc_req <frame len>" | zero padding | frame */ int len = atoi(custom + 14); - if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { + if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) { wpa_printf(MSG_DEBUG, "Invalid Manage.action event length %d", len); @@ -1384,7 +1387,7 @@ atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv, static void atheros_wireless_event_wireless(struct atheros_driver_data *drv, - char *data, int len) + char *data, unsigned int len) { struct iw_event iwe_buf, *iwe = &iwe_buf; char *pos, *end, *custom, *buf; @@ -1392,13 +1395,13 @@ atheros_wireless_event_wireless(struct atheros_driver_data *drv, pos = data; end = data + len; - while (pos + IW_EV_LCP_LEN <= end) { + while ((size_t) (end - pos) >= IW_EV_LCP_LEN) { /* Event data may be unaligned, so make a local, aligned copy * before processing. */ - memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d", iwe->cmd, iwe->len); - if (iwe->len <= IW_EV_LCP_LEN) + if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos) return; custom = pos + IW_EV_POINT_LEN; @@ -1409,10 +1412,10 @@ atheros_wireless_event_wireless(struct atheros_driver_data *drv, /* WE-19 removed the pointer from struct iw_point */ char *dpos = (char *) &iwe_buf.u.data.length; int dlen = dpos - (char *) &iwe_buf; - memcpy(dpos, pos + IW_EV_LCP_LEN, - sizeof(struct iw_event) - dlen); + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); } else { - memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); custom += IW_EV_POINT_OFF; } @@ -1430,12 +1433,12 @@ atheros_wireless_event_wireless(struct atheros_driver_data *drv, * just like IWEVCUSTOM. */ case IWEVCUSTOM: - if (custom + iwe->u.data.length > end) + if (iwe->u.data.length > end - custom) return; - buf = malloc(iwe->u.data.length + 1); + buf = os_malloc(iwe->u.data.length + 1); if (buf == NULL) return; /* XXX */ - memcpy(buf, custom, iwe->u.data.length); + os_memcpy(buf, custom, iwe->u.data.length); buf[iwe->u.data.length] = '\0'; if (iwe->u.data.flags != 0) { @@ -1446,7 +1449,7 @@ atheros_wireless_event_wireless(struct atheros_driver_data *drv, atheros_wireless_event_wireless_custom( drv, buf, buf + iwe->u.data.length); } - free(buf); + os_free(buf); break; } @@ -1500,7 +1503,7 @@ atheros_get_we_version(struct atheros_driver_data *drv) if (range == NULL) return -1; - memset(&iwr, 0, sizeof(iwr)); + os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.data.pointer = (caddr_t) range; iwr.u.data.length = buflen; @@ -1569,7 +1572,7 @@ atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, */ len = data_len + sizeof(struct l2_ethhdr); if (len > sizeof(buf)) { - bp = malloc(len); + bp = os_malloc(len); if (bp == NULL) { wpa_printf(MSG_INFO, "EAPOL frame discarded, cannot malloc temp buffer of size %lu!", @@ -1578,17 +1581,17 @@ atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, } } eth = (struct l2_ethhdr *) bp; - memcpy(eth->h_dest, addr, ETH_ALEN); - memcpy(eth->h_source, own_addr, ETH_ALEN); + os_memcpy(eth->h_dest, addr, ETH_ALEN); + os_memcpy(eth->h_source, own_addr, ETH_ALEN); eth->h_proto = host_to_be16(ETH_P_EAPOL); - memcpy(eth+1, data, data_len); + os_memcpy(eth + 1, data, data_len); wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); if (bp != buf) - free(bp); + os_free(bp); return status; } @@ -1622,9 +1625,9 @@ atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) strerror(errno)); goto bad; } - memcpy(drv->iface, params->ifname, sizeof(drv->iface)); + os_memcpy(drv->iface, params->ifname, sizeof(drv->iface)); - memset(&ifr, 0, sizeof(ifr)); + os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", @@ -1658,7 +1661,7 @@ atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) } else drv->sock_recv = drv->sock_xmit; - memset(&iwr, 0, sizeof(iwr)); + os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.mode = IW_MODE_MASTER; @@ -1704,10 +1707,10 @@ atheros_deinit(void *priv) atheros_reset_appfilter(drv); if (drv->wpa_ie || drv->wps_beacon_ie || drv->wps_probe_resp_ie) { + atheros_set_opt_ie(priv, NULL, 0); wpabuf_free(drv->wpa_ie); wpabuf_free(drv->wps_beacon_ie); wpabuf_free(drv->wps_probe_resp_ie); - atheros_set_opt_ie(priv, NULL, 0); } netlink_deinit(drv->netlink); (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); @@ -1728,7 +1731,7 @@ atheros_set_ssid(void *priv, const u8 *buf, int len) struct atheros_driver_data *drv = priv; struct iwreq iwr; - memset(&iwr, 0, sizeof(iwr)); + os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.essid.flags = 1; /* SSID active */ iwr.u.essid.pointer = (caddr_t) buf; @@ -1749,7 +1752,7 @@ atheros_get_ssid(void *priv, u8 *buf, int len) struct iwreq iwr; int ret = 0; - memset(&iwr, 0, sizeof(iwr)); + os_memset(&iwr, 0, sizeof(iwr)); os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.essid.pointer = (caddr_t) buf; iwr.u.essid.length = (len > IW_ESSID_MAX_SIZE) ? @@ -1848,7 +1851,8 @@ static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params) #if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len, - int noack, unsigned int freq) + int noack, unsigned int freq, + const u16 *csa_offs, size_t csa_offs_len) { struct atheros_driver_data *drv = priv; u8 buf[1510]; @@ -1859,7 +1863,7 @@ static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len, wpa_printf(MSG_DEBUG, "%s frmlen = %lu " MACSTR, __func__, (unsigned long) data_len, MAC2STR(mgmt->da)); mgmt_frm = (struct ieee80211req_mgmtbuf *) buf; - memcpy(mgmt_frm->macaddr, (u8 *)mgmt->da, IEEE80211_ADDR_LEN); + os_memcpy(mgmt_frm->macaddr, (u8 *)mgmt->da, IEEE80211_ADDR_LEN); mgmt_frm->buflen = data_len; if (&mgmt_frm->buf[0] + data_len > buf + sizeof(buf)) { wpa_printf(MSG_INFO, "atheros: Too long frame for " diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index bab1f031d24cd..2afd7df9637db 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -47,14 +47,25 @@ #include "l2_packet/l2_packet.h" +struct bsd_driver_global { + void *ctx; + int sock; /* socket for 802.11 ioctls */ + int route; /* routing socket for events */ + char *event_buf; + size_t event_buf_len; + struct dl_list ifaces; /* list of interfaces */ +}; + struct bsd_driver_data { + struct dl_list list; + struct bsd_driver_global *global; struct hostapd_data *hapd; /* back pointer */ - int sock; /* open socket for 802.11 ioctls */ struct l2_packet_data *sock_xmit;/* raw packet xmit socket */ - int route; /* routing socket for events */ char ifname[IFNAMSIZ+1]; /* interface name */ + int flags; unsigned int ifindex; /* interface index */ + int if_removed; /* has the interface been removed? */ void *ctx; struct wpa_driver_capa capa; /* driver capability */ int is_ap; /* Access point mode */ @@ -62,18 +73,47 @@ struct bsd_driver_data { int prev_privacy; /* privacy state to restore on deinit */ int prev_wpa; /* wpa state to restore on deinit */ enum ieee80211_opmode opmode; /* operation mode */ - char *event_buf; - size_t event_buf_len; }; /* Generic functions for hostapd and wpa_supplicant */ +static struct bsd_driver_data * +bsd_get_drvindex(void *priv, unsigned int ifindex) +{ + struct bsd_driver_global *global = priv; + struct bsd_driver_data *drv; + + dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) { + if (drv->ifindex == ifindex) + return drv; + } + return NULL; +} + +#ifndef HOSTAPD +static struct bsd_driver_data * +bsd_get_drvname(void *priv, const char *ifname) +{ + struct bsd_driver_global *global = priv; + struct bsd_driver_data *drv; + + dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) { + if (os_strcmp(drv->ifname, ifname) == 0) + return drv; + } + return NULL; +} +#endif /* HOSTAPD */ + static int bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len) { struct bsd_driver_data *drv = priv; struct ieee80211req ireq; + if (drv->ifindex == 0 || drv->if_removed) + return -1; + os_memset(&ireq, 0, sizeof(ireq)); os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name)); ireq.i_type = op; @@ -81,7 +121,7 @@ bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len) ireq.i_data = (void *) arg; ireq.i_len = arg_len; - if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) { + if (ioctl(drv->global->sock, SIOCS80211, &ireq) < 0) { wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, " "arg_len=%u]: %s", op, val, arg_len, strerror(errno)); @@ -102,7 +142,7 @@ bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg, ireq->i_len = arg_len; ireq->i_data = arg; - if (ioctl(drv->sock, SIOCG80211, ireq) < 0) { + if (ioctl(drv->global->sock, SIOCG80211, ireq) < 0) { wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, " "arg_len=%u]: %s", op, arg_len, strerror(errno)); return -1; @@ -143,7 +183,7 @@ bsd_get_ssid(void *priv, u8 *ssid, int len) os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); ifr.ifr_data = (void *)&nwid; - if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 || + if (ioctl(drv->global->sock, SIOCG80211NWID, &ifr) < 0 || nwid.i_len > IEEE80211_NWID_LEN) return -1; os_memcpy(ssid, nwid.i_nwid, nwid.i_len); @@ -166,7 +206,7 @@ bsd_set_ssid(void *priv, const u8 *ssid, int ssid_len) os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); ifr.ifr_data = (void *)&nwid; - return ioctl(drv->sock, SIOCS80211NWID, &ifr); + return ioctl(drv->global->sock, SIOCS80211NWID, &ifr); #else return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len); #endif @@ -181,7 +221,7 @@ bsd_get_if_media(void *priv) os_memset(&ifmr, 0, sizeof(ifmr)); os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name)); - if (ioctl(drv->sock, SIOCGIFMEDIA, &ifmr) < 0) { + if (ioctl(drv->global->sock, SIOCGIFMEDIA, &ifmr) < 0) { wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__, strerror(errno)); return -1; @@ -200,7 +240,7 @@ bsd_set_if_media(void *priv, int media) os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); ifr.ifr_media = media; - if (ioctl(drv->sock, SIOCSIFMEDIA, &ifr) < 0) { + if (ioctl(drv->global->sock, SIOCSIFMEDIA, &ifr) < 0) { wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__, strerror(errno)); return -1; @@ -263,11 +303,12 @@ bsd_ctrl_iface(void *priv, int enable) os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); - if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) { + if (ioctl(drv->global->sock, SIOCGIFFLAGS, &ifr) < 0) { wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", strerror(errno)); return -1; } + drv->flags = ifr.ifr_flags; if (enable) { if (ifr.ifr_flags & IFF_UP) @@ -279,12 +320,13 @@ bsd_ctrl_iface(void *priv, int enable) ifr.ifr_flags &= ~IFF_UP; } - if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) { + if (ioctl(drv->global->sock, SIOCSIFFLAGS, &ifr) < 0) { wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", strerror(errno)); return -1; } + drv->flags = ifr.ifr_flags; return 0; } @@ -576,7 +618,7 @@ bsd_set_freq(void *priv, struct hostapd_freq_params *freq) os_memset(&creq, 0, sizeof(creq)); os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name)); creq.i_channel = (u_int16_t)channel; - return ioctl(drv->sock, SIOCS80211CHANNEL, &creq); + return ioctl(drv->global->sock, SIOCS80211CHANNEL, &creq); #else /* SIOCS80211CHANNEL */ return set80211param(priv, IEEE80211_IOC_CHANNEL, channel); #endif /* SIOCS80211CHANNEL */ @@ -730,7 +772,8 @@ bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, static void bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) { - struct bsd_driver_data *drv = ctx; + struct bsd_driver_global *global = sock_ctx; + struct bsd_driver_data *drv; struct if_announcemsghdr *ifan; struct rt_msghdr *rtm; struct ieee80211_michael_event *mic; @@ -739,7 +782,7 @@ bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) int n; union wpa_event_data data; - n = read(sock, drv->event_buf, drv->event_buf_len); + n = read(sock, global->event_buf, global->event_buf_len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) wpa_printf(MSG_ERROR, "%s read() failed: %s", @@ -747,15 +790,18 @@ bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) return; } - rtm = (struct rt_msghdr *) drv->event_buf; + rtm = (struct rt_msghdr *) global->event_buf; if (rtm->rtm_version != RTM_VERSION) { wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", rtm->rtm_version); return; } - ifan = (struct if_announcemsghdr *) rtm; switch (rtm->rtm_type) { case RTM_IEEE80211: + ifan = (struct if_announcemsghdr *) rtm; + drv = bsd_get_drvindex(global, ifan->ifan_index); + if (drv == NULL) + return; switch (ifan->ifan_what) { case RTM_IEEE80211_ASSOC: case RTM_IEEE80211_REASSOC: @@ -811,21 +857,15 @@ bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params) return NULL; } - drv->event_buf_len = rtbuf_len(); - - drv->event_buf = os_malloc(drv->event_buf_len); - if (drv->event_buf == NULL) { - wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__); + drv->ifindex = if_nametoindex(params->ifname); + if (drv->ifindex == 0) { + wpa_printf(MSG_DEBUG, "%s: interface %s does not exist", + __func__, params->ifname); goto bad; } drv->hapd = hapd; - drv->sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->sock < 0) { - wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", - strerror(errno)); - goto bad; - } + drv->global = params->global_priv; os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname)); drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL, @@ -839,28 +879,18 @@ bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params) if (bsd_ctrl_iface(drv, 0) < 0) goto bad; - drv->route = socket(PF_ROUTE, SOCK_RAW, 0); - if (drv->route < 0) { - wpa_printf(MSG_ERROR, "socket(PF_ROUTE,SOCK_RAW): %s", - strerror(errno)); - goto bad; - } - eloop_register_read_sock(drv->route, bsd_wireless_event_receive, drv, - NULL); - if (bsd_set_mediaopt(drv, IFM_OMASK, IFM_IEEE80211_HOSTAP) < 0) { wpa_printf(MSG_ERROR, "%s: failed to set operation mode", __func__); goto bad; } + dl_list_add(&drv->global->ifaces, &drv->list); + return drv; bad: if (drv->sock_xmit != NULL) l2_packet_deinit(drv->sock_xmit); - if (drv->sock >= 0) - close(drv->sock); - os_free(drv->event_buf); os_free(drv); return NULL; } @@ -871,16 +901,10 @@ bsd_deinit(void *priv) { struct bsd_driver_data *drv = priv; - if (drv->route >= 0) { - eloop_unregister_read_sock(drv->route); - close(drv->route); - } - bsd_ctrl_iface(drv, 0); - if (drv->sock >= 0) - close(drv->sock); + if (drv->ifindex != 0) + bsd_ctrl_iface(drv, 0); if (drv->sock_xmit != NULL) l2_packet_deinit(drv->sock_xmit); - os_free(drv->event_buf); os_free(drv); } @@ -932,7 +956,7 @@ wpa_driver_bsd_get_bssid(void *priv, u8 *bssid) struct ieee80211_bssid bs; os_strlcpy(bs.i_name, drv->ifname, sizeof(bs.i_name)); - if (ioctl(drv->sock, SIOCG80211BSSID, &bs) < 0) + if (ioctl(drv->global->sock, SIOCG80211BSSID, &bs) < 0) return -1; os_memcpy(bssid, bs.i_bssid, sizeof(bs.i_bssid)); return 0; @@ -966,7 +990,7 @@ wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy) int ret = 0; wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d", - __FUNCTION__, wpa, privacy); + __func__, wpa, privacy); if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0) ret = -1; @@ -981,7 +1005,7 @@ wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy) static int wpa_driver_bsd_set_wpa(void *priv, int enabled) { - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled); } @@ -1186,7 +1210,8 @@ wpa_driver_bsd_scan(void *priv, struct wpa_driver_scan_params *params) static void wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) { - struct bsd_driver_data *drv = sock_ctx; + struct bsd_driver_global *global = sock_ctx; + struct bsd_driver_data *drv; struct if_announcemsghdr *ifan; struct if_msghdr *ifm; struct rt_msghdr *rtm; @@ -1196,7 +1221,7 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) struct ieee80211_join_event *join; int n; - n = read(sock, drv->event_buf, drv->event_buf_len); + n = read(sock, global->event_buf, global->event_buf_len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) wpa_printf(MSG_ERROR, "%s read() failed: %s", @@ -1204,7 +1229,7 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) return; } - rtm = (struct rt_msghdr *) drv->event_buf; + rtm = (struct rt_msghdr *) global->event_buf; if (rtm->rtm_version != RTM_VERSION) { wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", rtm->rtm_version); @@ -1214,53 +1239,79 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) switch (rtm->rtm_type) { case RTM_IFANNOUNCE: ifan = (struct if_announcemsghdr *) rtm; - if (ifan->ifan_index != drv->ifindex) - break; - os_strlcpy(event.interface_status.ifname, drv->ifname, - sizeof(event.interface_status.ifname)); switch (ifan->ifan_what) { case IFAN_DEPARTURE: + drv = bsd_get_drvindex(global, ifan->ifan_index); + if (drv) + drv->if_removed = 1; event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + break; + case IFAN_ARRIVAL: + drv = bsd_get_drvname(global, ifan->ifan_name); + if (drv) { + drv->ifindex = ifan->ifan_index; + drv->if_removed = 0; + } + event.interface_status.ievent = EVENT_INTERFACE_ADDED; + break; default: + wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: unknown action"); return; } wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s", - event.interface_status.ifname, + ifan->ifan_name, ifan->ifan_what == IFAN_DEPARTURE ? "removed" : "added"); - wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + os_strlcpy(event.interface_status.ifname, ifan->ifan_name, + sizeof(event.interface_status.ifname)); + if (drv) { + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, + &event); + /* + * Set ifindex to zero after sending the event as the + * event might query the driver to ensure a match. + */ + if (ifan->ifan_what == IFAN_DEPARTURE) + drv->ifindex = 0; + } else { + wpa_supplicant_event_global(global->ctx, + EVENT_INTERFACE_STATUS, + &event); + } break; case RTM_IEEE80211: ifan = (struct if_announcemsghdr *) rtm; - if (ifan->ifan_index != drv->ifindex) - break; + drv = bsd_get_drvindex(global, ifan->ifan_index); + if (drv == NULL) + return; switch (ifan->ifan_what) { case RTM_IEEE80211_ASSOC: case RTM_IEEE80211_REASSOC: if (drv->is_ap) break; - wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); break; case RTM_IEEE80211_DISASSOC: if (drv->is_ap) break; - wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); break; case RTM_IEEE80211_SCAN: if (drv->is_ap) break; - wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL); + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, + NULL); break; case RTM_IEEE80211_LEAVE: leave = (struct ieee80211_leave_event *) &ifan[1]; - drv_event_disassoc(ctx, leave->iev_addr); + drv_event_disassoc(drv->ctx, leave->iev_addr); break; case RTM_IEEE80211_JOIN: #ifdef RTM_IEEE80211_REJOIN case RTM_IEEE80211_REJOIN: #endif join = (struct ieee80211_join_event *) &ifan[1]; - bsd_new_sta(drv, ctx, join->iev_addr); + bsd_new_sta(drv, drv->ctx, join->iev_addr); break; case RTM_IEEE80211_REPLAY: /* ignore */ @@ -1275,23 +1326,30 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) os_memset(&event, 0, sizeof(event)); event.michael_mic_failure.unicast = !IEEE80211_IS_MULTICAST(mic->iev_dst); - wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, - &event); + wpa_supplicant_event(drv->ctx, + EVENT_MICHAEL_MIC_FAILURE, &event); break; } break; case RTM_IFINFO: ifm = (struct if_msghdr *) rtm; - if (ifm->ifm_index != drv->ifindex) - break; - if ((rtm->rtm_flags & RTF_UP) == 0) { - os_strlcpy(event.interface_status.ifname, drv->ifname, - sizeof(event.interface_status.ifname)); - event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + drv = bsd_get_drvindex(global, ifm->ifm_index); + if (drv == NULL) + return; + if ((ifm->ifm_flags & IFF_UP) == 0 && + (drv->flags & IFF_UP) != 0) { wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN", - event.interface_status.ifname); - wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + drv->ifname); + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, + NULL); + } else if ((ifm->ifm_flags & IFF_UP) != 0 && + (drv->flags & IFF_UP) == 0) { + wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' UP", + drv->ifname); + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, + NULL); } + drv->flags = ifm->ifm_flags; break; } } @@ -1318,11 +1376,16 @@ wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res, result->caps = sr->isr_capinfo; result->qual = sr->isr_rssi; result->noise = sr->isr_noise; + +#ifdef __FreeBSD__ /* * the rssi value reported by the kernel is in 0.5dB steps relative to * the reported noise floor. see ieee80211_node.h for details. */ result->level = sr->isr_rssi / 2 + sr->isr_noise; +#else + result->level = sr->isr_rssi; +#endif pos = (u8 *)(result + 1); @@ -1477,7 +1540,7 @@ get80211opmode(struct bsd_driver_data *drv) (void) memset(&ifmr, 0, sizeof(ifmr)); (void) os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name)); - if (ioctl(drv->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) { + if (ioctl(drv->global->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) { if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) { if (ifmr.ifm_current & IFM_FLAG0) return IEEE80211_M_AHDEMO; @@ -1497,7 +1560,7 @@ get80211opmode(struct bsd_driver_data *drv) } static void * -wpa_driver_bsd_init(void *ctx, const char *ifname) +wpa_driver_bsd_init(void *ctx, const char *ifname, void *priv) { #define GETPARAM(drv, param, v) \ (((v) = get80211param(drv, param)) != -1) @@ -1507,14 +1570,6 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) if (drv == NULL) return NULL; - drv->event_buf_len = rtbuf_len(); - - drv->event_buf = os_malloc(drv->event_buf_len); - if (drv->event_buf == NULL) { - wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__); - goto fail1; - } - /* * NB: We require the interface name be mappable to an index. * This implies we do not support having wpa_supplicant @@ -1525,24 +1580,12 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) if (drv->ifindex == 0) { wpa_printf(MSG_DEBUG, "%s: interface %s does not exist", __func__, ifname); - goto fail1; - } - drv->sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->sock < 0) - goto fail1; - - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - /* Down interface during setup. */ - if (bsd_ctrl_iface(drv, 0) < 0) - goto fail; - - drv->route = socket(PF_ROUTE, SOCK_RAW, 0); - if (drv->route < 0) goto fail; - eloop_register_read_sock(drv->route, - wpa_driver_bsd_event_receive, ctx, drv); + } drv->ctx = ctx; + drv->global = priv; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) { wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s", @@ -1563,13 +1606,15 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) if (wpa_driver_bsd_capa(drv)) goto fail; + /* Down interface during setup. */ + if (bsd_ctrl_iface(drv, 0) < 0) + goto fail; + drv->opmode = get80211opmode(drv); + dl_list_add(&drv->global->ifaces, &drv->list); return drv; fail: - close(drv->sock); -fail1: - os_free(drv->event_buf); os_free(drv); return NULL; #undef GETPARAM @@ -1580,22 +1625,25 @@ wpa_driver_bsd_deinit(void *priv) { struct bsd_driver_data *drv = priv; - wpa_driver_bsd_set_wpa(drv, 0); - eloop_unregister_read_sock(drv->route); + if (drv->ifindex != 0 && !drv->if_removed) { + wpa_driver_bsd_set_wpa(drv, 0); - /* NB: mark interface down */ - bsd_ctrl_iface(drv, 0); + /* NB: mark interface down */ + bsd_ctrl_iface(drv, 0); - wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy); - if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0) - wpa_printf(MSG_DEBUG, "%s: failed to restore roaming state", - __func__); + wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, + drv->prev_privacy); + + if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) + < 0) + wpa_printf(MSG_DEBUG, + "%s: failed to restore roaming state", + __func__); + } if (drv->sock_xmit != NULL) l2_packet_deinit(drv->sock_xmit); - (void) close(drv->route); /* ioctl socket */ - (void) close(drv->sock); /* event socket */ - os_free(drv->event_buf); + dl_list_del(&drv->list); os_free(drv); } @@ -1609,10 +1657,74 @@ wpa_driver_bsd_get_capa(void *priv, struct wpa_driver_capa *capa) } #endif /* HOSTAPD */ +static void * +bsd_global_init(void *ctx) +{ + struct bsd_driver_global *global; + + global = os_zalloc(sizeof(*global)); + if (global == NULL) + return NULL; + + global->ctx = ctx; + dl_list_init(&global->ifaces); + + global->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (global->sock < 0) { + wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); + goto fail1; + } + + global->route = socket(PF_ROUTE, SOCK_RAW, 0); + if (global->route < 0) { + wpa_printf(MSG_ERROR, "socket[PF_ROUTE,SOCK_RAW]: %s", + strerror(errno)); + goto fail; + } + + global->event_buf_len = rtbuf_len(); + global->event_buf = os_malloc(global->event_buf_len); + if (global->event_buf == NULL) { + wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__); + goto fail; + } + +#ifdef HOSTAPD + eloop_register_read_sock(global->route, bsd_wireless_event_receive, + NULL, global); + +#else /* HOSTAPD */ + eloop_register_read_sock(global->route, wpa_driver_bsd_event_receive, + NULL, global); +#endif /* HOSTAPD */ + + return global; + +fail: + close(global->sock); +fail1: + os_free(global); + return NULL; +} + +static void +bsd_global_deinit(void *priv) +{ + struct bsd_driver_global *global = priv; + + eloop_unregister_read_sock(global->route); + (void) close(global->route); + (void) close(global->sock); + os_free(global); +} + const struct wpa_driver_ops wpa_driver_bsd_ops = { .name = "bsd", .desc = "BSD 802.11 support", + .global_init = bsd_global_init, + .global_deinit = bsd_global_deinit, #ifdef HOSTAPD .hapd_init = bsd_init, .hapd_deinit = bsd_deinit, @@ -1625,7 +1737,7 @@ const struct wpa_driver_ops wpa_driver_bsd_ops = { .sta_set_flags = bsd_set_sta_authorized, .commit = bsd_commit, #else /* HOSTAPD */ - .init = wpa_driver_bsd_init, + .init2 = wpa_driver_bsd_init, .deinit = wpa_driver_bsd_deinit, .get_bssid = wpa_driver_bsd_get_bssid, .get_ssid = wpa_driver_bsd_get_ssid, diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index aebea8cf64e37..c7107ba899b0b 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -80,6 +80,7 @@ const char * event_to_string(enum wpa_event_type event) E2S(NEW_PEER_CANDIDATE); E2S(ACS_CHANNEL_SELECTED); E2S(DFS_CAC_STARTED); + E2S(P2P_LO_STOP); } return "UNKNOWN"; @@ -183,12 +184,12 @@ wpa_get_wowlan_triggers(const char *wowlan_triggers, start = buf; while (*start != '\0') { - while (isblank(*start)) + while (isblank((unsigned char) *start)) start++; if (*start == '\0') break; end = start; - while (!isblank(*end) && *end != '\0') + while (!isblank((unsigned char) *end) && *end != '\0') end++; last = *end == '\0'; *end = '\0'; @@ -218,3 +219,55 @@ out: os_free(buf); return triggers; } + + +const char * driver_flag_to_string(u64 flag) +{ +#define DF2S(x) case WPA_DRIVER_FLAGS_ ## x: return #x + switch (flag) { + DF2S(DRIVER_IE); + DF2S(SET_KEYS_AFTER_ASSOC); + DF2S(DFS_OFFLOAD); + DF2S(4WAY_HANDSHAKE); + DF2S(WIRED); + DF2S(SME); + DF2S(AP); + DF2S(SET_KEYS_AFTER_ASSOC_DONE); + DF2S(HT_2040_COEX); + DF2S(P2P_CONCURRENT); + DF2S(P2P_DEDICATED_INTERFACE); + DF2S(P2P_CAPABLE); + DF2S(AP_TEARDOWN_SUPPORT); + DF2S(P2P_MGMT_AND_NON_P2P); + DF2S(SANE_ERROR_CODES); + DF2S(OFFCHANNEL_TX); + DF2S(EAPOL_TX_STATUS); + DF2S(DEAUTH_TX_STATUS); + DF2S(BSS_SELECTION); + DF2S(TDLS_SUPPORT); + DF2S(TDLS_EXTERNAL_SETUP); + DF2S(PROBE_RESP_OFFLOAD); + DF2S(AP_UAPSD); + DF2S(INACTIVITY_TIMER); + DF2S(AP_MLME); + DF2S(SAE); + DF2S(OBSS_SCAN); + DF2S(IBSS); + DF2S(RADAR); + DF2S(DEDICATED_P2P_DEVICE); + DF2S(QOS_MAPPING); + DF2S(AP_CSA); + DF2S(MESH); + DF2S(ACS_OFFLOAD); + DF2S(KEY_MGMT_OFFLOAD); + DF2S(TDLS_CHANNEL_SWITCH); + DF2S(HT_IBSS); + DF2S(VHT_IBSS); + DF2S(SUPPORT_HW_MODE_ANY); + DF2S(OFFCHANNEL_SIMULTANEOUS); + DF2S(FULL_AP_CLIENT_STATE); + DF2S(P2P_LISTEN_OFFLOAD); + } + return "UNKNOWN"; +#undef DF2S +} diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c index a7aa5eff00bd9..517a3bbb5d308 100644 --- a/src/drivers/driver_hostap.c +++ b/src/drivers/driver_hostap.c @@ -258,7 +258,8 @@ static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr) static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack, - unsigned int freq) + unsigned int freq, + const u16 *csa_offs, size_t csa_offs_len) { struct hostap_driver_data *drv = priv; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg; @@ -307,7 +308,7 @@ static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data, pos += 2; memcpy(pos, data, data_len); - res = hostap_send_mlme(drv, (u8 *) hdr, len, 0, 0); + res = hostap_send_mlme(drv, (u8 *) hdr, len, 0, 0, NULL, 0); if (res < 0) { wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - " "failed: %d (%s)", @@ -813,7 +814,7 @@ hostapd_wireless_event_wireless_custom(struct hostap_driver_data *drv, static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv, - char *data, int len) + char *data, unsigned int len) { struct iw_event iwe_buf, *iwe = &iwe_buf; char *pos, *end, *custom, *buf; @@ -821,13 +822,13 @@ static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv, pos = data; end = data + len; - while (pos + IW_EV_LCP_LEN <= end) { + while ((size_t) (end - pos) >= IW_EV_LCP_LEN) { /* Event data may be unaligned, so make a local, aligned copy * before processing. */ memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", iwe->cmd, iwe->len); - if (iwe->len <= IW_EV_LCP_LEN) + if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos) return; custom = pos + IW_EV_POINT_LEN; @@ -846,7 +847,7 @@ static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv, switch (iwe->cmd) { case IWEVCUSTOM: - if (custom + iwe->u.data.length > end) + if (iwe->u.data.length > end - custom) return; buf = malloc(iwe->u.data.length + 1); if (buf == NULL) @@ -1045,7 +1046,7 @@ static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, memcpy(mgmt.bssid, own_addr, ETH_ALEN); mgmt.u.deauth.reason_code = host_to_le16(reason); return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.deauth), 0, 0); + sizeof(mgmt.u.deauth), 0, 0, NULL, 0); } @@ -1083,7 +1084,7 @@ static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, memcpy(mgmt.bssid, own_addr, ETH_ALEN); mgmt.u.disassoc.reason_code = host_to_le16(reason); return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.disassoc), 0, 0); + sizeof(mgmt.u.disassoc), 0, 0, NULL, 0); } @@ -1161,7 +1162,7 @@ static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr, os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); - hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0); + hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0, NULL, 0); } diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c index 3eae2f89d20e8..826d3cc621335 100644 --- a/src/drivers/driver_macsec_qca.c +++ b/src/drivers/driver_macsec_qca.c @@ -11,6 +11,7 @@ #include "includes.h" #include <sys/ioctl.h> #include <net/if.h> +#include <inttypes.h> #ifdef __linux__ #include <netpacket/packet.h> #include <net/if_arp.h> @@ -485,15 +486,12 @@ static int macsec_qca_set_replay_protect(void *priv, Boolean enabled, } -static int macsec_qca_set_current_cipher_suite(void *priv, const u8 *cs, - size_t cs_len) +static int macsec_qca_set_current_cipher_suite(void *priv, u64 cs) { - u8 default_cs_id[] = CS_ID_GCM_AES_128; - - if (cs_len != CS_ID_LEN || - os_memcmp(cs, default_cs_id, cs_len) != 0) { - wpa_hexdump(MSG_ERROR, "macsec: NOT supported CipherSuite", - cs, cs_len); + if (cs != CS_ID_GCM_AES_128) { + wpa_printf(MSG_ERROR, + "%s: NOT supported CipherSuite: %016" PRIx64, + __func__, cs); return -1; } diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c index 669f1b813c43f..9440f0127235a 100644 --- a/src/drivers/driver_ndis.c +++ b/src/drivers/driver_ndis.c @@ -35,6 +35,7 @@ int close(int fd); #include "driver.h" #include "eloop.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" #include "driver_ndis.h" int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv); @@ -780,20 +781,7 @@ static int wpa_driver_ndis_scan(void *priv, static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) { - const u8 *end, *pos; - - pos = (const u8 *) (res + 1); - end = pos + res->ie_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; + return get_ie((const u8 *) (res + 1), res->ie_len, ie); } diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 00b173f3f85a3..1210d43560877 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -176,13 +176,19 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, static int nl80211_send_frame_cmd(struct i802_bss *bss, unsigned int freq, unsigned int wait, const u8 *buf, size_t buf_len, u64 *cookie, - int no_cck, int no_ack, int offchanok); + int no_cck, int no_ack, int offchanok, + const u16 *csa_offs, size_t csa_offs_len); static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report); -static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); -static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); -static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); +#define IFIDX_ANY -1 + +static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, + int ifidx_reason); +static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, + int ifidx_reason); +static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, + int ifidx_reason); static int nl80211_set_channel(struct i802_bss *bss, struct hostapd_freq_params *freq, int set_chan); @@ -194,6 +200,10 @@ static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv, static int i802_set_iface_flags(struct i802_bss *bss, int up); static int nl80211_set_param(void *priv, const char *param); +#ifdef CONFIG_MESH +static int nl80211_put_mesh_config(struct nl_msg *msg, + struct wpa_driver_mesh_bss_params *params); +#endif /* CONFIG_MESH */ /* Converts nl80211_chan_width to a common format */ @@ -439,6 +449,8 @@ static int nl_get_multicast_id(struct nl80211_global *global, void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg, int flags, uint8_t cmd) { + if (TEST_FAIL()) + return NULL; return genlmsg_put(msg, 0, 0, drv->global->nl80211_id, 0, flags, cmd, 0); } @@ -757,6 +769,15 @@ static void nl80211_put_wiphy_data_ap(struct i802_bss *bss) } +static unsigned int nl80211_get_ifindex(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + + return drv->ifindex; +} + + static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) { struct i802_bss *bss = priv; @@ -780,11 +801,12 @@ static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) static void wpa_driver_nl80211_event_newlink( - struct wpa_driver_nl80211_data *drv, const char *ifname) + struct nl80211_global *global, struct wpa_driver_nl80211_data *drv, + int ifindex, const char *ifname) { union wpa_event_data event; - if (os_strcmp(drv->first_bss->ifname, ifname) == 0) { + if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) { if (if_nametoindex(drv->first_bss->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Interface %s does not exist - ignore RTM_NEWLINK", drv->first_bss->ifname); @@ -798,19 +820,25 @@ static void wpa_driver_nl80211_event_newlink( } os_memset(&event, 0, sizeof(event)); + event.interface_status.ifindex = ifindex; os_strlcpy(event.interface_status.ifname, ifname, sizeof(event.interface_status.ifname)); event.interface_status.ievent = EVENT_INTERFACE_ADDED; - wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); + if (drv) + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); + else + wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS, + &event); } static void wpa_driver_nl80211_event_dellink( - struct wpa_driver_nl80211_data *drv, const char *ifname) + struct nl80211_global *global, struct wpa_driver_nl80211_data *drv, + int ifindex, const char *ifname) { union wpa_event_data event; - if (os_strcmp(drv->first_bss->ifname, ifname) == 0) { + if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) { if (drv->if_removed) { wpa_printf(MSG_DEBUG, "nl80211: if_removed already set - ignore RTM_DELLINK event for %s", ifname); @@ -825,10 +853,15 @@ static void wpa_driver_nl80211_event_dellink( } os_memset(&event, 0, sizeof(event)); + event.interface_status.ifindex = ifindex; os_strlcpy(event.interface_status.ifname, ifname, sizeof(event.interface_status.ifname)); event.interface_status.ievent = EVENT_INTERFACE_REMOVED; - wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); + if (drv) + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); + else + wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS, + &event); } @@ -882,7 +915,7 @@ nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len) dl_list_for_each(drv, &global->interfaces, struct wpa_driver_nl80211_data, list) { if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) || - have_ifidx(drv, idx)) + have_ifidx(drv, idx, IFIDX_ANY)) return drv; } return NULL; @@ -902,13 +935,6 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, char ifname[IFNAMSIZ + 1]; char extra[100], *pos, *end; - drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); - if (!drv) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_NEWLINK event for foreign ifindex %d", - ifi->ifi_index); - return; - } - extra[0] = '\0'; pos = extra; end = pos + sizeof(extra); @@ -952,6 +978,10 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); + if (!drv) + goto event_newlink; + if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { namebuf[0] = '\0'; if (if_indextoname(ifi->ifi_index, namebuf) && @@ -1046,10 +1076,12 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, -1, IF_OPER_UP); } +event_newlink: if (ifname[0]) - wpa_driver_nl80211_event_newlink(drv, ifname); + wpa_driver_nl80211_event_newlink(global, drv, ifi->ifi_index, + ifname); - if (ifi->ifi_family == AF_BRIDGE && brid) { + if (ifi->ifi_family == AF_BRIDGE && brid && drv) { struct i802_bss *bss; /* device has been added to bridge */ @@ -1061,7 +1093,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, } wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s", brid, namebuf); - add_ifidx(drv, brid); + add_ifidx(drv, brid, ifi->ifi_index); for (bss = drv->first_bss; bss; bss = bss->next) { if (os_strcmp(ifname, bss->ifname) == 0) { @@ -1085,13 +1117,6 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, char ifname[IFNAMSIZ + 1]; char extra[100], *pos, *end; - drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); - if (!drv) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_DELLINK event for foreign ifindex %d", - ifi->ifi_index); - return; - } - extra[0] = '\0'; pos = extra; end = pos + sizeof(extra); @@ -1132,10 +1157,9 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); - if (ifname[0] && (ifi->ifi_family != AF_BRIDGE || !brid)) - wpa_driver_nl80211_event_dellink(drv, ifname); + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); - if (ifi->ifi_family == AF_BRIDGE && brid) { + if (ifi->ifi_family == AF_BRIDGE && brid && drv) { /* device has been removed from bridge */ char namebuf[IFNAMSIZ]; @@ -1148,8 +1172,12 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, "nl80211: Remove ifindex %u for bridge %s", brid, namebuf); } - del_ifidx(drv, brid); + del_ifidx(drv, brid, ifi->ifi_index); } + + if (ifi->ifi_family != AF_BRIDGE || !brid) + wpa_driver_nl80211_event_dellink(global, drv, ifi->ifi_index, + ifname); } @@ -1519,11 +1547,16 @@ static void nl80211_check_global(struct nl80211_global *global) static void wpa_driver_nl80211_rfkill_blocked(void *ctx) { + struct wpa_driver_nl80211_data *drv = ctx; + wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); + /* - * This may be for any interface; use ifdown event to disable - * interface. + * rtnetlink ifdown handler will report interfaces other than the P2P + * Device interface as disabled. */ + if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL); } @@ -1536,7 +1569,16 @@ static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) "after rfkill unblock"); return; } - /* rtnetlink ifup handler will report interface as enabled */ + + if (is_p2p_net_interface(drv->nlmode)) + nl80211_disable_11b_rates(drv, drv->ifindex, 1); + + /* + * rtnetlink ifup handler will report interfaces other than the P2P + * Device interface as enabled. + */ + if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL); } @@ -1621,13 +1663,65 @@ static void nl80211_destroy_bss(struct i802_bss *bss) } +static void +wpa_driver_nl80211_drv_init_rfkill(struct wpa_driver_nl80211_data *drv) +{ + struct rfkill_config *rcfg; + + if (drv->rfkill) + return; + + rcfg = os_zalloc(sizeof(*rcfg)); + if (!rcfg) + return; + + rcfg->ctx = drv; + + /* rfkill uses netdev sysfs for initialization. However, P2P Device is + * not associated with a netdev, so use the name of some other interface + * sharing the same wiphy as the P2P Device interface. + * + * Note: This is valid, as a P2P Device interface is always dynamically + * created and is created only once another wpa_s interface was added. + */ + if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) { + struct nl80211_global *global = drv->global; + struct wpa_driver_nl80211_data *tmp1; + + dl_list_for_each(tmp1, &global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (drv == tmp1 || drv->wiphy_idx != tmp1->wiphy_idx || + !tmp1->rfkill) + continue; + + wpa_printf(MSG_DEBUG, + "nl80211: Use (%s) to initialize P2P Device rfkill", + tmp1->first_bss->ifname); + os_strlcpy(rcfg->ifname, tmp1->first_bss->ifname, + sizeof(rcfg->ifname)); + break; + } + } else { + os_strlcpy(rcfg->ifname, drv->first_bss->ifname, + sizeof(rcfg->ifname)); + } + + rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked; + rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked; + drv->rfkill = rfkill_init(rcfg); + if (!drv->rfkill) { + wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available"); + os_free(rcfg); + } +} + + static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, void *global_priv, int hostapd, const u8 *set_addr, const char *driver_params) { struct wpa_driver_nl80211_data *drv; - struct rfkill_config *rcfg; struct i802_bss *bss; if (global_priv == NULL) @@ -1649,6 +1743,7 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); drv->if_indices = drv->default_if_indices; + drv->if_indices_reason = drv->default_if_indices_reason; drv->first_bss = os_zalloc(sizeof(*drv->first_bss)); if (!drv->first_bss) { @@ -1668,22 +1763,6 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, if (nl80211_init_bss(bss)) goto failed; - rcfg = os_zalloc(sizeof(*rcfg)); - if (rcfg == NULL) - goto failed; - rcfg->ctx = drv; - os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname)); - rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked; - rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked; - drv->rfkill = rfkill_init(rcfg); - if (drv->rfkill == NULL) { - wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available"); - os_free(rcfg); - } - - if (linux_iface_up(drv->global->ioctl_sock, ifname) > 0) - drv->start_iface_up = 1; - if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params)) goto failed; @@ -1916,6 +1995,10 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) if (nl80211_register_action_frame(bss, (u8 *) "\x05\x05", 2) < 0) ret = -1; + /* Radio Measurement - Radio Measurement Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x05\x00", 2) < 0) + ret = -1; + /* Radio Measurement - Link Measurement Request */ if ((drv->capa.rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) && (nl80211_register_action_frame(bss, (u8 *) "\x05\x02", 2) < 0)) @@ -1977,6 +2060,49 @@ static int nl80211_register_spurious_class3(struct i802_bss *bss) } +static int nl80211_action_subscribe_ap(struct i802_bss *bss) +{ + int ret = 0; + + /* Public Action frames */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04", 1) < 0) + ret = -1; + /* RRM Measurement Report */ + if (nl80211_register_action_frame(bss, (u8 *) "\x05\x01", 2) < 0) + ret = -1; + /* RRM Neighbor Report Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x05\x04", 2) < 0) + ret = -1; + /* FT Action frames */ + if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0) + ret = -1; +#ifdef CONFIG_IEEE80211W + /* SA Query */ + if (nl80211_register_action_frame(bss, (u8 *) "\x08", 1) < 0) + ret = -1; +#endif /* CONFIG_IEEE80211W */ + /* Protected Dual of Public Action */ + if (nl80211_register_action_frame(bss, (u8 *) "\x09", 1) < 0) + ret = -1; + /* WNM */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0a", 1) < 0) + ret = -1; + /* WMM */ + if (nl80211_register_action_frame(bss, (u8 *) "\x11", 1) < 0) + ret = -1; +#ifdef CONFIG_FST + /* FST Action frames */ + if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0) + ret = -1; +#endif /* CONFIG_FST */ + /* Vendor-specific */ + if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0) + ret = -1; + + return ret; +} + + static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) { static const int stypes[] = { @@ -1985,7 +2111,6 @@ static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) WLAN_FC_STYPE_REASSOC_REQ, WLAN_FC_STYPE_DISASSOC, WLAN_FC_STYPE_DEAUTH, - WLAN_FC_STYPE_ACTION, WLAN_FC_STYPE_PROBE_REQ, /* Beacon doesn't work as mac80211 doesn't currently allow * it, but it wouldn't really be the right thing anyway as @@ -2010,6 +2135,9 @@ static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) } } + if (nl80211_action_subscribe_ap(bss)) + goto out_err; + if (nl80211_register_spurious_class3(bss)) goto out_err; @@ -2032,10 +2160,7 @@ static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss) wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP " "handle %p (device SME)", bss->nl_mgmt); - if (nl80211_register_frame(bss, bss->nl_mgmt, - (WLAN_FC_TYPE_MGMT << 2) | - (WLAN_FC_STYPE_ACTION << 4), - NULL, 0) < 0) + if (nl80211_action_subscribe_ap(bss)) goto out_err; nl80211_mgmt_handle_register_eloop(bss); @@ -2186,6 +2311,11 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP) bss->static_ap = 1; + if (first && + nl80211_get_ifmode(bss) != NL80211_IFTYPE_P2P_DEVICE && + linux_iface_up(drv->global->ioctl_sock, bss->ifname) > 0) + drv->start_iface_up = 1; + if (wpa_driver_nl80211_capa(drv)) return -1; @@ -2206,7 +2336,8 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, if (drv->hostapd || bss->static_ap) nlmode = NL80211_IFTYPE_AP; - else if (bss->if_dynamic) + else if (bss->if_dynamic || + nl80211_get_ifmode(bss) == NL80211_IFTYPE_MESH_POINT) nlmode = nl80211_get_ifmode(bss); else nlmode = NL80211_IFTYPE_STATION; @@ -2219,6 +2350,8 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, if (nlmode == NL80211_IFTYPE_P2P_DEVICE) nl80211_get_macaddr(bss); + wpa_driver_nl80211_drv_init_rfkill(drv); + if (!rfkill_is_blocked(drv->rfkill)) { int ret = i802_set_iface_flags(bss, 1); if (ret) { @@ -2226,25 +2359,32 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, "interface '%s' UP", bss->ifname); return ret; } + + if (is_p2p_net_interface(nlmode)) + nl80211_disable_11b_rates(bss->drv, + bss->drv->ifindex, 1); + if (nlmode == NL80211_IFTYPE_P2P_DEVICE) return ret; } else { wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable " "interface '%s' due to rfkill", bss->ifname); - if (nlmode == NL80211_IFTYPE_P2P_DEVICE) - return 0; - drv->if_disabled = 1; + if (nlmode != NL80211_IFTYPE_P2P_DEVICE) + drv->if_disabled = 1; + send_rfkill_event = 1; } - if (!drv->hostapd) + if (!drv->hostapd && nlmode != NL80211_IFTYPE_P2P_DEVICE) netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 1, IF_OPER_DORMANT); - if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, - bss->addr)) - return -1; - os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN); + if (nlmode != NL80211_IFTYPE_P2P_DEVICE) { + if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + bss->addr)) + return -1; + os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN); + } if (send_rfkill_event) { eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, @@ -2279,6 +2419,7 @@ static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv) static void wpa_driver_nl80211_deinit(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; + unsigned int i; wpa_printf(MSG_INFO, "nl80211: deinit ifname=%s disabled_11b_rates=%d", bss->ifname, drv->disabled_11b_rates); @@ -2325,6 +2466,9 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) if (drv->if_indices != drv->default_if_indices) os_free(drv->if_indices); + if (drv->if_indices_reason != drv->default_if_indices_reason) + os_free(drv->if_indices_reason); + if (drv->disabled_11b_rates) nl80211_disable_11b_rates(drv, drv->ifindex, 0); @@ -2372,6 +2516,10 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) os_free(drv->extended_capa); os_free(drv->extended_capa_mask); + for (i = 0; i < drv->num_iface_ext_capa; i++) { + os_free(drv->iface_ext_capa[i].ext_capa); + os_free(drv->iface_ext_capa[i].ext_capa_mask); + } os_free(drv->first_bss); os_free(drv); } @@ -2468,6 +2616,7 @@ static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[], } +#ifdef CONFIG_DRIVER_NL80211_QCA static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, const u8 *key, size_t key_len) { @@ -2495,6 +2644,7 @@ static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, return ret; } +#endif /* CONFIG_DRIVER_NL80211_QCA */ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, @@ -2525,6 +2675,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, } #endif /* CONFIG_TDLS */ +#ifdef CONFIG_DRIVER_NL80211_QCA if (alg == WPA_ALG_PMK && (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) { wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key", @@ -2532,6 +2683,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, ret = issue_key_mgmt_set_key(drv, key, key_len); return ret; } +#endif /* CONFIG_DRIVER_NL80211_QCA */ if (alg == WPA_ALG_NONE) { msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY); @@ -3089,7 +3241,9 @@ static int wpa_driver_nl80211_send_frame(struct i802_bss *bss, const void *data, size_t len, int encrypt, int noack, unsigned int freq, int no_cck, - int offchanok, unsigned int wait_time) + int offchanok, unsigned int wait_time, + const u16 *csa_offs, + size_t csa_offs_len) { struct wpa_driver_nl80211_data *drv = bss->drv; u64 cookie; @@ -3115,7 +3269,8 @@ static int wpa_driver_nl80211_send_frame(struct i802_bss *bss, wpa_printf(MSG_DEBUG, "nl80211: send_frame -> send_frame_cmd"); res = nl80211_send_frame_cmd(bss, freq, wait_time, data, len, - &cookie, no_cck, noack, offchanok); + &cookie, no_cck, noack, offchanok, + csa_offs, csa_offs_len); if (res == 0 && !noack) { const struct ieee80211_mgmt *mgmt; u16 fc; @@ -3141,7 +3296,9 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, size_t data_len, int noack, unsigned int freq, int no_cck, int offchanok, - unsigned int wait_time) + unsigned int wait_time, + const u16 *csa_offs, + size_t csa_offs_len) { struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt *mgmt; @@ -3171,7 +3328,7 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, } return nl80211_send_frame_cmd(bss, freq, 0, data, data_len, NULL, 1, noack, - 1); + 1, csa_offs, csa_offs_len); } if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) { @@ -3185,7 +3342,8 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, wait_time, data, data_len, &drv->send_action_cookie, - no_cck, noack, offchanok); + no_cck, noack, offchanok, + csa_offs, csa_offs_len); } if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && @@ -3205,7 +3363,8 @@ static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame"); return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, noack, freq, no_cck, offchanok, - wait_time); + wait_time, csa_offs, + csa_offs_len); } @@ -3314,6 +3473,48 @@ static int nl80211_put_beacon_int(struct nl_msg *msg, int beacon_int) } +static int nl80211_put_dtim_period(struct nl_msg *msg, int dtim_period) +{ + if (dtim_period > 0) { + wpa_printf(MSG_DEBUG, " * dtim_period=%d", dtim_period); + return nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period); + } + + return 0; +} + + +#ifdef CONFIG_MESH +static int nl80211_set_mesh_config(void *priv, + struct wpa_driver_mesh_bss_params *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MESH_CONFIG); + if (!msg) + return -1; + + ret = nl80211_put_mesh_config(msg, params); + if (ret < 0) { + nlmsg_free(msg); + return ret; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Mesh config set failed: %d (%s)", + ret, strerror(-ret)); + return ret; + } + return 0; +} +#endif /* CONFIG_MESH */ + + static int wpa_driver_nl80211_set_ap(void *priv, struct wpa_driver_ap_params *params) { @@ -3327,6 +3528,9 @@ static int wpa_driver_nl80211_set_ap(void *priv, int smps_mode; u32 suites[10], suite; u32 ver; +#ifdef CONFIG_MESH + struct wpa_driver_mesh_bss_params mesh_params; +#endif /* CONFIG_MESH */ beacon_set = params->reenable ? 0 : bss->beacon_set; @@ -3350,7 +3554,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail) || nl80211_put_beacon_int(msg, params->beacon_int) || - nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period) || + nl80211_put_dtim_period(msg, params->dtim_period) || nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) goto fail; if (params->proberesp && params->proberesp_len) { @@ -3421,8 +3625,10 @@ static int wpa_driver_nl80211_set_ap(void *priv, goto fail; if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X_NO_WPA && - params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40) && - nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)) + (!params->pairwise_ciphers || + params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) && + (nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) || + nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))) goto fail; wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x", @@ -3441,24 +3647,26 @@ static int wpa_driver_nl80211_set_ap(void *priv, nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite)) goto fail; - switch (params->smps_mode) { - case HT_CAP_INFO_SMPS_DYNAMIC: - wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - dynamic"); - smps_mode = NL80211_SMPS_DYNAMIC; - break; - case HT_CAP_INFO_SMPS_STATIC: - wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - static"); - smps_mode = NL80211_SMPS_STATIC; - break; - default: - /* invalid - fallback to smps off */ - case HT_CAP_INFO_SMPS_DISABLED: - wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - off"); - smps_mode = NL80211_SMPS_OFF; - break; + if (params->ht_opmode != -1) { + switch (params->smps_mode) { + case HT_CAP_INFO_SMPS_DYNAMIC: + wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - dynamic"); + smps_mode = NL80211_SMPS_DYNAMIC; + break; + case HT_CAP_INFO_SMPS_STATIC: + wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - static"); + smps_mode = NL80211_SMPS_STATIC; + break; + default: + /* invalid - fallback to smps off */ + case HT_CAP_INFO_SMPS_DISABLED: + wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - off"); + smps_mode = NL80211_SMPS_OFF; + break; + } + if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode)) + goto fail; } - if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode)) - goto fail; if (params->beacon_ies) { wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies", @@ -3508,6 +3716,12 @@ static int wpa_driver_nl80211_set_ap(void *priv, } #endif /* CONFIG_P2P */ + if (params->pbss) { + wpa_printf(MSG_DEBUG, "nl80211: PBSS"); + if (nla_put_flag(msg, NL80211_ATTR_PBSS)) + goto fail; + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", @@ -3533,7 +3747,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, "nl80211: Frequency set succeeded for ht2040 coex"); bss->bandwidth = params->freq->bandwidth; } - } else if (!beacon_set) { + } else if (!beacon_set && params->freq) { /* * cfg80211 updates the driver on frequence change in AP * mode only at the point when beaconing is started, so @@ -3542,6 +3756,18 @@ static int wpa_driver_nl80211_set_ap(void *priv, bss->bandwidth = params->freq->bandwidth; } } + +#ifdef CONFIG_MESH + if (is_mesh_interface(drv->nlmode) && params->ht_opmode != -1) { + os_memset(&mesh_params, 0, sizeof(mesh_params)); + mesh_params.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE; + mesh_params.ht_opmode = params->ht_opmode; + ret = nl80211_set_mesh_config(priv, &mesh_params); + if (ret < 0) + return ret; + } +#endif /* CONFIG_MESH */ + return ret; fail: nlmsg_free(msg); @@ -3615,6 +3841,12 @@ static int nl80211_put_freq_params(struct nl_msg *msg, wpa_printf(MSG_DEBUG, " * channel_type=%d", ct); if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ct)) return -ENOBUFS; + } else { + wpa_printf(MSG_DEBUG, " * channel_type=%d", + NL80211_CHAN_NO_HT); + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_NO_HT)) + return -ENOBUFS; } return 0; } @@ -3666,6 +3898,8 @@ static u32 sta_flags_nl80211(int flags) f |= BIT(NL80211_STA_FLAG_TDLS_PEER); if (flags & WPA_STA_AUTHENTICATED) f |= BIT(NL80211_STA_FLAG_AUTHENTICATED); + if (flags & WPA_STA_ASSOCIATED) + f |= BIT(NL80211_STA_FLAG_ASSOCIATED); return f; } @@ -3675,11 +3909,11 @@ static u32 sta_flags_nl80211(int flags) static u32 sta_plink_state_nl80211(enum mesh_plink_state state) { switch (state) { - case PLINK_LISTEN: + case PLINK_IDLE: return NL80211_PLINK_LISTEN; - case PLINK_OPEN_SENT: + case PLINK_OPN_SNT: return NL80211_PLINK_OPN_SNT; - case PLINK_OPEN_RCVD: + case PLINK_OPN_RCVD: return NL80211_PLINK_OPN_RCVD; case PLINK_CNF_RCVD: return NL80211_PLINK_CNF_RCVD; @@ -3718,7 +3952,17 @@ static int wpa_driver_nl80211_sta_add(void *priv, if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr)) goto fail; - if (!params->set || (params->flags & WPA_STA_TDLS_PEER)) { + /* + * Set the below properties only in one of the following cases: + * 1. New station is added, already associated. + * 2. Set WPA_STA_TDLS_PEER station. + * 3. Set an already added unassociated station, if driver supports + * full AP client state. (Set these properties after station became + * associated will be rejected by the driver). + */ + if (!params->set || (params->flags & WPA_STA_TDLS_PEER) || + (params->set && FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) && + (params->flags & WPA_STA_ASSOCIATED))) { wpa_hexdump(MSG_DEBUG, " * supported rates", params->supp_rates, params->supp_rates_len); wpa_printf(MSG_DEBUG, " * capability=0x%x", @@ -3756,6 +4000,13 @@ static int wpa_driver_nl80211_sta_add(void *priv, params->ext_capab_len, params->ext_capab)) goto fail; } + + if (is_ap_interface(drv->nlmode) && + nla_put_u8(msg, NL80211_ATTR_STA_SUPPORT_P2P_PS, + params->support_p2p_ps ? + NL80211_P2P_PS_SUPPORTED : + NL80211_P2P_PS_UNSUPPORTED)) + goto fail; } if (!params->set) { if (params->aid) { @@ -3766,9 +4017,12 @@ static int wpa_driver_nl80211_sta_add(void *priv, /* * cfg80211 validates that AID is non-zero, so we have * to make this a non-zero value for the TDLS case where - * a dummy STA entry is used for now. + * a dummy STA entry is used for now and for a station + * that is still not associated. */ - wpa_printf(MSG_DEBUG, " * aid=1 (TDLS workaround)"); + wpa_printf(MSG_DEBUG, " * aid=1 (%s workaround)", + (params->flags & WPA_STA_TDLS_PEER) ? + "TDLS" : "UNASSOC_STA"); if (nla_put_u16(msg, NL80211_ATTR_STA_AID, 1)) goto fail; } @@ -3781,6 +4035,15 @@ static int wpa_driver_nl80211_sta_add(void *priv, wpa_printf(MSG_DEBUG, " * peer_aid=%u", params->aid); if (nla_put_u16(msg, NL80211_ATTR_PEER_AID, params->aid)) goto fail; + } else if (FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) && + (params->flags & WPA_STA_ASSOCIATED)) { + wpa_printf(MSG_DEBUG, " * aid=%u", params->aid); + wpa_printf(MSG_DEBUG, " * listen_interval=%u", + params->listen_interval); + if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid) || + nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, + params->listen_interval)) + goto fail; } if (params->vht_opmode_enabled) { @@ -3811,6 +4074,45 @@ static int wpa_driver_nl80211_sta_add(void *priv, os_memset(&upd, 0, sizeof(upd)); upd.set = sta_flags_nl80211(params->flags); upd.mask = upd.set | sta_flags_nl80211(params->flags_mask); + + /* + * If the driver doesn't support full AP client state, ignore ASSOC/AUTH + * flags, as nl80211 driver moves a new station, by default, into + * associated state. + * + * On the other hand, if the driver supports that feature and the + * station is added in unauthenticated state, set the + * authenticated/associated bits in the mask to prevent moving this + * station to associated state before it is actually associated. + * + * This is irrelevant for mesh mode where the station is added to the + * driver as authenticated already, and ASSOCIATED isn't part of the + * nl80211 API. + */ + if (!is_mesh_interface(drv->nlmode)) { + if (!FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) { + wpa_printf(MSG_DEBUG, + "nl80211: Ignore ASSOC/AUTH flags since driver doesn't support full AP client state"); + upd.mask &= ~(BIT(NL80211_STA_FLAG_ASSOCIATED) | + BIT(NL80211_STA_FLAG_AUTHENTICATED)); + } else if (!params->set && + !(params->flags & WPA_STA_TDLS_PEER)) { + if (!(params->flags & WPA_STA_AUTHENTICATED)) + upd.mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED); + if (!(params->flags & WPA_STA_ASSOCIATED)) + upd.mask |= BIT(NL80211_STA_FLAG_ASSOCIATED); + } +#ifdef CONFIG_MESH + } else { + if (params->plink_state == PLINK_ESTAB && params->peer_aid) { + ret = nla_put_u16(msg, NL80211_ATTR_MESH_PEER_AID, + params->peer_aid); + if (ret) + goto fail; + } +#endif /* CONFIG_MESH */ + } + wpa_printf(MSG_DEBUG, " * flags set=0x%x mask=0x%x", upd.set, upd.mask); if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) @@ -3933,7 +4235,11 @@ void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx) /* stop listening for EAPOL on this interface */ dl_list_for_each(drv2, &drv->global->interfaces, struct wpa_driver_nl80211_data, list) - del_ifidx(drv2, ifidx); + { + del_ifidx(drv2, ifidx, IFIDX_ANY); + /* Remove all bridges learned for this iface */ + del_ifidx(drv2, IFIDX_ANY, ifidx); + } msg = nl80211_ifindex_msg(drv, ifidx, 0, NL80211_CMD_DEL_INTERFACE); if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) @@ -3942,7 +4248,7 @@ void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx) } -static const char * nl80211_iftype_str(enum nl80211_iftype mode) +const char * nl80211_iftype_str(enum nl80211_iftype mode) { switch (mode) { case NL80211_IFTYPE_ADHOC: @@ -4041,7 +4347,7 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, iftype == NL80211_IFTYPE_WDS || iftype == NL80211_IFTYPE_MONITOR) { /* start listening for EAPOL on this interface */ - add_ifidx(drv, ifidx); + add_ifidx(drv, ifidx, IFIDX_ANY); } if (addr && iftype != NL80211_IFTYPE_MONITOR && @@ -4124,7 +4430,8 @@ static int nl80211_setup_ap(struct i802_bss *bss) if (drv->device_ap_sme && !drv->use_monitor) if (nl80211_mgmt_subscribe_ap_dev_sme(bss)) - return -1; + wpa_printf(MSG_DEBUG, + "nl80211: Failed to subscribe for mgmt frames from SME driver - trying to run without it"); if (!drv->device_ap_sme && drv->use_monitor && nl80211_create_monitor_interface(drv) && @@ -4244,7 +4551,7 @@ static int wpa_driver_nl80211_hapd_send_eapol( memcpy(pos, data, data_len); res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0, - 0, 0, 0, 0); + 0, 0, 0, 0, NULL, 0); if (res < 0) { wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - " "failed: %d (%s)", @@ -4473,8 +4780,9 @@ retry: goto fail; } - if (nl80211_ht_vht_overrides(msg, params) < 0) - return -1; + ret = nl80211_ht_vht_overrides(msg, params); + if (ret < 0) + goto fail; ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; @@ -4648,15 +4956,24 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT)) return -1; + if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_NO_WPA && + (params->pairwise_suite == WPA_CIPHER_NONE || + params->pairwise_suite == WPA_CIPHER_WEP104 || + params->pairwise_suite == WPA_CIPHER_WEP40) && + (nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) || + nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))) + return -1; + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED && nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED)) return -1; if (params->rrm_used) { u32 drv_rrm_flags = drv->capa.rrm_flags; - if (!(drv_rrm_flags & - WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) || - !(drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET) || + if ((!((drv_rrm_flags & + WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) && + (drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) && + !(drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_RRM)) || nla_put_flag(msg, NL80211_ATTR_USE_RRM)) return -1; } @@ -4667,6 +4984,22 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, if (params->p2p) wpa_printf(MSG_DEBUG, " * P2P group"); + if (params->pbss) { + wpa_printf(MSG_DEBUG, " * PBSS"); + if (nla_put_flag(msg, NL80211_ATTR_PBSS)) + return -1; + } + + drv->connect_reassoc = 0; + if (params->prev_bssid) { + wpa_printf(MSG_DEBUG, " * prev_bssid=" MACSTR, + MAC2STR(params->prev_bssid)); + if (nla_put(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN, + params->prev_bssid)) + return -1; + drv->connect_reassoc = 1; + } + return 0; } @@ -4680,6 +5013,7 @@ static int wpa_driver_nl80211_try_connect( int ret; int algs; +#ifdef CONFIG_DRIVER_NL80211_QCA if (params->req_key_mgmt_offload && params->psk && (params->key_mgmt_suite == WPA_KEY_MGMT_PSK || params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || @@ -4689,6 +5023,7 @@ static int wpa_driver_nl80211_try_connect( if (ret) return ret; } +#endif /* CONFIG_DRIVER_NL80211_QCA */ wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex); msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT); @@ -4817,14 +5152,6 @@ static int wpa_driver_nl80211_associate( if (ret) goto fail; - if (params->prev_bssid) { - wpa_printf(MSG_DEBUG, " * prev_bssid=" MACSTR, - MAC2STR(params->prev_bssid)); - if (nla_put(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN, - params->prev_bssid)) - goto fail; - } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { @@ -4880,6 +5207,9 @@ static int wpa_driver_nl80211_set_mode_impl( int res; int mode_switch_res; + if (TEST_FAIL()) + return -1; + mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode); if (mode_switch_res && nlmode == nl80211_get_ifmode(bss)) mode_switch_res = 0; @@ -5236,6 +5566,8 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 }, + [NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 }, + [NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 }, }; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), @@ -5261,10 +5593,23 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) if (stats[NL80211_STA_INFO_INACTIVE_TIME]) data->inactive_msec = nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]); + /* For backwards compatibility, fetch the 32-bit counters first. */ if (stats[NL80211_STA_INFO_RX_BYTES]) data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]); if (stats[NL80211_STA_INFO_TX_BYTES]) data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]); + if (stats[NL80211_STA_INFO_RX_BYTES64] && + stats[NL80211_STA_INFO_TX_BYTES64]) { + /* + * The driver supports 64-bit counters, so use them to override + * the 32-bit values. + */ + data->rx_bytes = + nla_get_u64(stats[NL80211_STA_INFO_RX_BYTES64]); + data->tx_bytes = + nla_get_u64(stats[NL80211_STA_INFO_TX_BYTES64]); + data->bytes_64bit = 1; + } if (stats[NL80211_STA_INFO_RX_PACKETS]) data->rx_packets = nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]); @@ -5433,7 +5778,7 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.deauth), 0, 0, 0, 0, - 0); + 0, NULL, 0); } @@ -5460,7 +5805,7 @@ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.disassoc), 0, 0, 0, 0, - 0); + 0, NULL, 0); } @@ -5475,7 +5820,9 @@ static void dump_ifidx(struct wpa_driver_nl80211_data *drv) for (i = 0; i < drv->num_if_indices; i++) { if (!drv->if_indices[i]) continue; - res = os_snprintf(pos, end - pos, " %d", drv->if_indices[i]); + res = os_snprintf(pos, end - pos, " %d(%d)", + drv->if_indices[i], + drv->if_indices_reason[i]); if (os_snprintf_error(end - pos, res)) break; pos += res; @@ -5487,14 +5834,16 @@ static void dump_ifidx(struct wpa_driver_nl80211_data *drv) } -static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, + int ifidx_reason) { int i; - int *old; + int *old, *old_reason; - wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d", - ifidx); - if (have_ifidx(drv, ifidx)) { + wpa_printf(MSG_DEBUG, + "nl80211: Add own interface ifindex %d (ifidx_reason %d)", + ifidx, ifidx_reason); + if (have_ifidx(drv, ifidx, ifidx_reason)) { wpa_printf(MSG_DEBUG, "nl80211: ifindex %d already in the list", ifidx); return; @@ -5502,6 +5851,7 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) for (i = 0; i < drv->num_if_indices; i++) { if (drv->if_indices[i] == 0) { drv->if_indices[i] = ifidx; + drv->if_indices_reason[i] = ifidx_reason; dump_ifidx(drv); return; } @@ -5512,32 +5862,57 @@ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) else old = NULL; + if (drv->if_indices_reason != drv->default_if_indices_reason) + old_reason = drv->if_indices_reason; + else + old_reason = NULL; + drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1, sizeof(int)); + drv->if_indices_reason = os_realloc_array(old_reason, + drv->num_if_indices + 1, + sizeof(int)); if (!drv->if_indices) { if (!old) drv->if_indices = drv->default_if_indices; else drv->if_indices = old; + } + if (!drv->if_indices_reason) { + if (!old_reason) + drv->if_indices_reason = drv->default_if_indices_reason; + else + drv->if_indices_reason = old_reason; + } + if (!drv->if_indices || !drv->if_indices_reason) { wpa_printf(MSG_ERROR, "Failed to reallocate memory for " "interfaces"); wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx); return; - } else if (!old) + } + if (!old) os_memcpy(drv->if_indices, drv->default_if_indices, sizeof(drv->default_if_indices)); + if (!old_reason) + os_memcpy(drv->if_indices_reason, + drv->default_if_indices_reason, + sizeof(drv->default_if_indices_reason)); drv->if_indices[drv->num_if_indices] = ifidx; + drv->if_indices_reason[drv->num_if_indices] = ifidx_reason; drv->num_if_indices++; dump_ifidx(drv); } -static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, + int ifidx_reason) { int i; for (i = 0; i < drv->num_if_indices; i++) { - if (drv->if_indices[i] == ifidx) { + if ((drv->if_indices[i] == ifidx || ifidx == IFIDX_ANY) && + (drv->if_indices_reason[i] == ifidx_reason || + ifidx_reason == IFIDX_ANY)) { drv->if_indices[i] = 0; break; } @@ -5546,12 +5921,15 @@ static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) } -static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx) +static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx, + int ifidx_reason) { int i; for (i = 0; i < drv->num_if_indices; i++) - if (drv->if_indices[i] == ifidx) + if (drv->if_indices[i] == ifidx && + (drv->if_indices_reason[i] == ifidx_reason || + ifidx_reason == IFIDX_ANY)) return 1; return 0; @@ -5616,7 +5994,7 @@ static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) return; } - if (have_ifidx(drv, lladdr.sll_ifindex)) + if (have_ifidx(drv, lladdr.sll_ifindex, IFIDX_ANY)) drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len); } @@ -5643,7 +6021,7 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, } bss->added_bridge = 1; br_ifindex = if_nametoindex(brname); - add_ifidx(drv, br_ifindex); + add_ifidx(drv, br_ifindex, drv->ifindex); } bss->br_ifindex = br_ifindex; @@ -5705,7 +6083,15 @@ static void *i802_init(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in master %s", params->ifname, master_ifname); /* start listening for EAPOL on the master interface */ - add_ifidx(drv, if_nametoindex(master_ifname)); + add_ifidx(drv, if_nametoindex(master_ifname), drv->ifindex); + + /* check if master itself is under bridge */ + if (linux_br_get(master_ifname, master_ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: which is in bridge %s", + master_ifname); + br_ifindex = if_nametoindex(master_ifname); + os_strlcpy(bss->brname, master_ifname, IFNAMSIZ); + } } else { master_ifname[0] = '\0'; } @@ -5716,14 +6102,14 @@ static void *i802_init(struct hostapd_data *hapd, if (params->bridge[i]) { ifindex = if_nametoindex(params->bridge[i]); if (ifindex) - add_ifidx(drv, ifindex); + add_ifidx(drv, ifindex, drv->ifindex); if (ifindex == br_ifindex) br_added = 1; } } /* start listening for EAPOL on the default AP interface */ - add_ifidx(drv, drv->ifindex); + add_ifidx(drv, drv->ifindex, IFIDX_ANY); if (params->num_bridge && params->bridge[0]) { if (i802_check_bridge(drv, bss, params->bridge[0], @@ -5735,7 +6121,7 @@ static void *i802_init(struct hostapd_data *hapd, if (!br_added && br_ifindex && (params->num_bridge == 0 || !params->bridge[0])) - add_ifidx(drv, br_ifindex); + add_ifidx(drv, br_ifindex, drv->ifindex); #ifdef CONFIG_LIBNL3_ROUTE if (bss->added_if_into_bridge) { @@ -5880,7 +6266,8 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, const char *ifname, const u8 *addr, void *bss_ctx, void **drv_priv, char *force_ifname, u8 *if_addr, - const char *bridge, int use_existing) + const char *bridge, int use_existing, + int setup_ap) { enum nl80211_iftype nlmode; struct i802_bss *bss = priv; @@ -5964,7 +6351,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, os_memcpy(if_addr, new_addr, ETH_ALEN); } - if (type == WPA_IF_AP_BSS) { + if (type == WPA_IF_AP_BSS && setup_ap) { struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss)); if (new_bss == NULL) { if (added) @@ -6020,7 +6407,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, nlmode == NL80211_IFTYPE_AP_VLAN || nlmode == NL80211_IFTYPE_WDS || nlmode == NL80211_IFTYPE_MONITOR)) - add_ifidx(drv, ifidx); + add_ifidx(drv, ifidx, IFIDX_ANY); return 0; } @@ -6040,8 +6427,10 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, else if (ifindex > 0 && !bss->added_if) { struct wpa_driver_nl80211_data *drv2; dl_list_for_each(drv2, &drv->global->interfaces, - struct wpa_driver_nl80211_data, list) - del_ifidx(drv2, ifindex); + struct wpa_driver_nl80211_data, list) { + del_ifidx(drv2, ifindex, IFIDX_ANY); + del_ifidx(drv2, IFIDX_ANY, ifindex); + } } if (type != WPA_IF_AP_BSS) @@ -6119,7 +6508,8 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss, unsigned int freq, unsigned int wait, const u8 *buf, size_t buf_len, u64 *cookie_out, int no_cck, int no_ack, - int offchanok) + int offchanok, const u16 *csa_offs, + size_t csa_offs_len) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; @@ -6139,6 +6529,8 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss, nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) || (no_cck && nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) || (no_ack && nla_put_flag(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK)) || + (csa_offs && nla_put(msg, NL80211_ATTR_CSA_C_OFFSETS_TX, + csa_offs_len * sizeof(u16), csa_offs)) || nla_put(msg, NL80211_ATTR_FRAME, buf_len, buf)) goto fail; @@ -6156,6 +6548,20 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss, if (cookie_out) *cookie_out = no_ack ? (u64) -1 : cookie; + + if (drv->num_send_action_cookies == MAX_SEND_ACTION_COOKIES) { + wpa_printf(MSG_DEBUG, + "nl80211: Drop oldest pending send action cookie 0x%llx", + (long long unsigned int) + drv->send_action_cookies[0]); + os_memmove(&drv->send_action_cookies[0], + &drv->send_action_cookies[1], + (MAX_SEND_ACTION_COOKIES - 1) * + sizeof(u64)); + drv->num_send_action_cookies--; + } + drv->send_action_cookies[drv->num_send_action_cookies] = cookie; + drv->num_send_action_cookies++; } fail: @@ -6198,29 +6604,28 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss, !drv->use_monitor)) ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len, 0, freq, no_cck, 1, - wait_time); + wait_time, NULL, 0); else ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf, 24 + data_len, &drv->send_action_cookie, - no_cck, 0, 1); + no_cck, 0, 1, NULL, 0); os_free(buf); return ret; } -static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) +static void nl80211_frame_wait_cancel(struct i802_bss *bss, u64 cookie) { - struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx", - (long long unsigned int) drv->send_action_cookie); + (long long unsigned int) cookie); if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME_WAIT_CANCEL)) || - nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie)) { + nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) { nlmsg_free(msg); return; } @@ -6232,6 +6637,30 @@ static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) } +static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + unsigned int i; + u64 cookie; + + /* Cancel the last pending TX cookie */ + nl80211_frame_wait_cancel(bss, drv->send_action_cookie); + + /* + * Cancel the other pending TX cookies, if any. This is needed since + * the driver may keep a list of all pending offchannel TX operations + * and free up the radio only once they have expired or cancelled. + */ + for (i = drv->num_send_action_cookies; i > 0; i--) { + cookie = drv->send_action_cookies[i - 1]; + if (cookie != drv->send_action_cookie) + nl80211_frame_wait_cancel(bss, cookie); + } + drv->num_send_action_cookies = 0; +} + + static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, unsigned int duration) { @@ -6454,9 +6883,13 @@ static int wpa_driver_nl80211_deinit_p2p_cli(void *priv) static void wpa_driver_nl80211_resume(void *priv) { struct i802_bss *bss = priv; + enum nl80211_iftype nlmode = nl80211_get_ifmode(bss); if (i802_set_iface_flags(bss, 1)) wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event"); + + if (is_p2p_net_interface(nlmode)) + nl80211_disable_11b_rates(bss->drv, bss->drv->ifindex, 1); } @@ -6529,8 +6962,12 @@ static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) os_memset(si, 0, sizeof(*si)); res = nl80211_get_link_signal(drv, si); - if (res != 0) - return res; + if (res) { + if (drv->nlmode != NL80211_IFTYPE_ADHOC && + drv->nlmode != NL80211_IFTYPE_MESH_POINT) + return res; + si->current_signal = 0; + } res = nl80211_get_channel_width(drv, si); if (res != 0) @@ -6545,21 +6982,21 @@ static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, { struct i802_bss *bss = priv; return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0, - 0, 0, 0, 0); + 0, 0, 0, 0, NULL, 0); } static int nl80211_set_param(void *priv, const char *param) { - wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param); + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (param == NULL) return 0; + wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param); #ifdef CONFIG_P2P if (os_strstr(param, "use_p2p_group_interface=1")) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " "interface"); drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; @@ -6567,22 +7004,18 @@ static int nl80211_set_param(void *priv, const char *param) } #endif /* CONFIG_P2P */ - if (os_strstr(param, "use_monitor=1")) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; + if (os_strstr(param, "use_monitor=1")) drv->use_monitor = 1; - } if (os_strstr(param, "force_connect_cmd=1")) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME; drv->force_connect_cmd = 1; } + if (os_strstr(param, "force_bss_selection=1")) + drv->capa.flags |= WPA_DRIVER_FLAGS_BSS_SELECTION; + if (os_strstr(param, "no_offchannel_tx=1")) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_TX; drv->test_use_roc_tx = 1; } @@ -6591,7 +7024,7 @@ static int nl80211_set_param(void *priv, const char *param) } -static void * nl80211_global_init(void) +static void * nl80211_global_init(void *ctx) { struct nl80211_global *global; struct netlink_config *cfg; @@ -6599,6 +7032,7 @@ static void * nl80211_global_init(void) global = os_zalloc(sizeof(*global)); if (global == NULL) return NULL; + global->ctx = ctx; global->ioctl_sock = -1; dl_list_init(&global->interfaces); global->if_add_ifindex = -1; @@ -6954,7 +7388,7 @@ static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr, os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0, - 0, 0) < 0) + 0, 0, NULL, 0) < 0) wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to " "send poll frame"); } @@ -7224,6 +7658,19 @@ static int driver_nl80211_scan2(void *priv, struct wpa_driver_scan_params *params) { struct i802_bss *bss = priv; +#ifdef CONFIG_DRIVER_NL80211_QCA + struct wpa_driver_nl80211_data *drv = bss->drv; + + /* + * Do a vendor specific scan if possible. If only_new_results is + * set, do a normal scan since a kernel (cfg80211) BSS cache flush + * cannot be achieved through a vendor scan. The below condition may + * need to be modified if new scan flags are added in the future whose + * functionality can only be achieved through a normal scan. + */ + if (drv->scan_vendor_cmd_avail && !params->only_new_results) + return wpa_driver_nl80211_vendor_scan(bss, params); +#endif /* CONFIG_DRIVER_NL80211_QCA */ return wpa_driver_nl80211_scan(bss, params); } @@ -7261,11 +7708,13 @@ static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type, static int driver_nl80211_send_mlme(void *priv, const u8 *data, size_t data_len, int noack, - unsigned int freq) + unsigned int freq, + const u16 *csa_offs, size_t csa_offs_len) { struct i802_bss *bss = priv; return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack, - freq, 0, 0, 0); + freq, 0, 0, 0, csa_offs, + csa_offs_len); } @@ -7340,7 +7789,7 @@ static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md, } -const u8 * wpa_driver_nl80211_get_macaddr(void *priv) +static const u8 * wpa_driver_nl80211_get_macaddr(void *priv) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -7495,7 +7944,10 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) "capa.mac_addr_rand_scan_supported=%d\n" "capa.conc_capab=%u\n" "capa.max_conc_chan_2_4=%u\n" - "capa.max_conc_chan_5_0=%u\n", + "capa.max_conc_chan_5_0=%u\n" + "capa.max_sched_scan_plans=%u\n" + "capa.max_sched_scan_plan_interval=%u\n" + "capa.max_sched_scan_plan_iterations=%u\n", drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth, @@ -7514,7 +7966,10 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) drv->capa.mac_addr_rand_scan_supported, drv->capa.conc_capab, drv->capa.max_conc_chan_2_4, - drv->capa.max_conc_chan_5_0); + drv->capa.max_conc_chan_5_0, + drv->capa.max_sched_scan_plans, + drv->capa.max_sched_scan_plan_interval, + drv->capa.max_sched_scan_plan_iterations); if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; @@ -7557,6 +8012,8 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) struct wpa_driver_nl80211_data *drv = bss->drv; struct nlattr *beacon_csa; int ret = -ENOBUFS; + int csa_off_len = 0; + int i; wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d width=%d cf1=%d cf2=%d)", settings->cs_count, settings->block_tx, @@ -7573,21 +8030,57 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) (drv->nlmode != NL80211_IFTYPE_P2P_GO)) return -EOPNOTSUPP; - /* check settings validity */ - if (!settings->beacon_csa.tail || - ((settings->beacon_csa.tail_len <= - settings->counter_offset_beacon) || - (settings->beacon_csa.tail[settings->counter_offset_beacon] != - settings->cs_count))) + /* + * Remove empty counters, assuming Probe Response and Beacon frame + * counters match. This implementation assumes that there are only two + * counters. + */ + if (settings->counter_offset_beacon[0] && + !settings->counter_offset_beacon[1]) { + csa_off_len = 1; + } else if (settings->counter_offset_beacon[1] && + !settings->counter_offset_beacon[0]) { + csa_off_len = 1; + settings->counter_offset_beacon[0] = + settings->counter_offset_beacon[1]; + settings->counter_offset_presp[0] = + settings->counter_offset_presp[1]; + } else if (settings->counter_offset_beacon[1] && + settings->counter_offset_beacon[0]) { + csa_off_len = 2; + } else { + wpa_printf(MSG_ERROR, "nl80211: No CSA counters provided"); + return -EINVAL; + } + + /* Check CSA counters validity */ + if (drv->capa.max_csa_counters && + csa_off_len > drv->capa.max_csa_counters) { + wpa_printf(MSG_ERROR, + "nl80211: Too many CSA counters provided"); return -EINVAL; + } - if (settings->beacon_csa.probe_resp && - ((settings->beacon_csa.probe_resp_len <= - settings->counter_offset_presp) || - (settings->beacon_csa.probe_resp[settings->counter_offset_presp] != - settings->cs_count))) + if (!settings->beacon_csa.tail) return -EINVAL; + for (i = 0; i < csa_off_len; i++) { + u16 csa_c_off_bcn = settings->counter_offset_beacon[i]; + u16 csa_c_off_presp = settings->counter_offset_presp[i]; + + if ((settings->beacon_csa.tail_len <= csa_c_off_bcn) || + (settings->beacon_csa.tail[csa_c_off_bcn] != + settings->cs_count)) + return -EINVAL; + + if (settings->beacon_csa.probe_resp && + ((settings->beacon_csa.probe_resp_len <= + csa_c_off_presp) || + (settings->beacon_csa.probe_resp[csa_c_off_presp] != + settings->cs_count))) + return -EINVAL; + } + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CHANNEL_SWITCH)) || nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT, settings->cs_count) || @@ -7610,11 +8103,13 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) if (ret) goto error; - if (nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_BEACON, - settings->counter_offset_beacon) || + if (nla_put(msg, NL80211_ATTR_CSA_C_OFF_BEACON, + csa_off_len * sizeof(u16), + settings->counter_offset_beacon) || (settings->beacon_csa.probe_resp && - nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_PRESP, - settings->counter_offset_presp))) + nla_put(msg, NL80211_ATTR_CSA_C_OFF_PRESP, + csa_off_len * sizeof(u16), + settings->counter_offset_presp))) goto fail; nla_nest_end(msg, beacon_csa); @@ -7860,6 +8355,7 @@ static int nl80211_set_wowlan(void *priv, } +#ifdef CONFIG_DRIVER_NL80211_QCA static int nl80211_roaming(void *priv, int allowed, const u8 *bssid) { struct i802_bss *bss = priv; @@ -7892,6 +8388,7 @@ static int nl80211_roaming(void *priv, int allowed, const u8 *bssid) return send_and_recv_msgs(drv, msg, NULL, NULL); } +#endif /* CONFIG_DRIVER_NL80211_QCA */ static int nl80211_set_mac_addr(void *priv, const u8 *addr) @@ -7900,6 +8397,9 @@ static int nl80211_set_mac_addr(void *priv, const u8 *addr) struct wpa_driver_nl80211_data *drv = bss->drv; int new_addr = addr != NULL; + if (TEST_FAIL()) + return -1; + if (!addr) addr = drv->perm_addr; @@ -7960,6 +8460,46 @@ static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id, } +static int nl80211_put_mesh_config(struct nl_msg *msg, + struct wpa_driver_mesh_bss_params *params) +{ + struct nlattr *container; + + container = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG); + if (!container) + return -1; + + if (((params->flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) && + nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, + params->auto_plinks)) || + ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS) && + nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS, + params->max_peer_links))) + return -1; + + /* + * Set NL80211_MESHCONF_PLINK_TIMEOUT even if user mpm is used because + * the timer could disconnect stations even in that case. + */ + if ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT) && + nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT, + params->peer_link_timeout)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to set PLINK_TIMEOUT"); + return -1; + } + + if ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE) && + nla_put_u16(msg, NL80211_MESHCONF_HT_OPMODE, params->ht_opmode)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to set HT_OP_MODE"); + return -1; + } + + nla_nest_end(msg, container); + + return 0; +} + + static int nl80211_join_mesh(struct i802_bss *bss, struct wpa_driver_mesh_join_params *params) { @@ -7974,7 +8514,8 @@ static int nl80211_join_mesh(struct i802_bss *bss, nl80211_put_freq_params(msg, ¶ms->freq) || nl80211_put_basic_rates(msg, params->basic_rates) || nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) || - nl80211_put_beacon_int(msg, params->beacon_int)) + nl80211_put_beacon_int(msg, params->beacon_int) || + nl80211_put_dtim_period(msg, params->dtim_period)) goto fail; wpa_printf(MSG_DEBUG, " * flags=%08X", params->flags); @@ -8003,30 +8544,12 @@ static int nl80211_join_mesh(struct i802_bss *bss, goto fail; nla_nest_end(msg, container); - container = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG); - if (!container) + params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS; + params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT; + params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS; + if (nl80211_put_mesh_config(msg, ¶ms->conf) < 0) goto fail; - if (!(params->conf.flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) && - nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, 0)) - goto fail; - if ((params->conf.flags & WPA_DRIVER_MESH_FLAG_DRIVER_MPM) && - nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS, - params->max_peer_links)) - goto fail; - - /* - * Set NL80211_MESHCONF_PLINK_TIMEOUT even if user mpm is used because - * the timer could disconnect stations even in that case. - */ - if (nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT, - params->conf.peer_link_timeout)) { - wpa_printf(MSG_ERROR, "nl80211: Failed to set PLINK_TIMEOUT"); - goto fail; - } - - nla_nest_end(msg, container); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { @@ -8035,7 +8558,7 @@ static int nl80211_join_mesh(struct i802_bss *bss, goto fail; } ret = 0; - bss->freq = params->freq.freq; + drv->assoc_freq = bss->freq = params->freq.freq; wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully"); fail: @@ -8376,6 +8899,8 @@ set_val: } +#ifdef CONFIG_DRIVER_NL80211_QCA + static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode) { switch (hw_mode) { @@ -8700,6 +9225,215 @@ static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq) } +static int nl80211_p2p_lo_start(void *priv, unsigned int freq, + unsigned int period, unsigned int interval, + unsigned int count, const u8 *device_types, + size_t dev_types_len, + const u8 *ies, size_t ies_len) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *container; + int ret; + + wpa_printf(MSG_DEBUG, + "nl80211: Start P2P Listen offload: freq=%u, period=%u, interval=%u, count=%u", + freq, period, interval, count); + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD)) + 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_P2P_LISTEN_OFFLOAD_START)) + goto fail; + + container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!container) + goto fail; + + if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL, + freq) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD, + period) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL, + interval) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT, + count) || + nla_put(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES, + dev_types_len, device_types) || + nla_put(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE, + ies_len, ies)) + goto fail; + + nla_nest_end(msg, container); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Failed to send P2P Listen offload vendor command"); + goto fail; + } + + return 0; + +fail: + nlmsg_free(msg); + return -1; +} + + +static int nl80211_p2p_lo_stop(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + wpa_printf(MSG_DEBUG, "nl80211: Stop P2P Listen offload"); + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD)) + 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_P2P_LISTEN_OFFLOAD_STOP)) { + nlmsg_free(msg); + return -1; + } + + return send_and_recv_msgs(drv, msg, NULL, NULL); +} + +#endif /* CONFIG_DRIVER_NL80211_QCA */ + + +static int nl80211_write_to_file(const char *name, unsigned int val) +{ + int fd, len; + char tmp[128]; + + fd = open(name, O_RDWR); + if (fd < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to open %s: %s", + name, strerror(errno)); + return fd; + } + + len = os_snprintf(tmp, sizeof(tmp), "%u\n", val); + len = write(fd, tmp, len); + if (len < 0) + wpa_printf(MSG_ERROR, "nl80211: Failed to write to %s: %s", + name, strerror(errno)); + close(fd); + + return 0; +} + + +static int nl80211_configure_data_frame_filters(void *priv, u32 filter_flags) +{ + struct i802_bss *bss = priv; + char path[128]; + int ret; + + wpa_printf(MSG_DEBUG, "nl80211: Data frame filter flags=0x%x", + filter_flags); + + /* Configure filtering of unicast frame encrypted using GTK */ + ret = os_snprintf(path, sizeof(path), + "/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast", + bss->ifname); + if (os_snprintf_error(sizeof(path), ret)) + return -1; + + ret = nl80211_write_to_file(path, + !!(filter_flags & + WPA_DATA_FRAME_FILTER_FLAG_GTK)); + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to set IPv4 unicast in multicast filter"); + return ret; + } + + os_snprintf(path, sizeof(path), + "/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast", + bss->ifname); + ret = nl80211_write_to_file(path, + !!(filter_flags & + WPA_DATA_FRAME_FILTER_FLAG_GTK)); + + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to set IPv6 unicast in multicast filter"); + return ret; + } + + /* Configure filtering of unicast frame encrypted using GTK */ + os_snprintf(path, sizeof(path), + "/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp", + bss->ifname); + ret = nl80211_write_to_file(path, + !!(filter_flags & + WPA_DATA_FRAME_FILTER_FLAG_ARP)); + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Failed set gratuitous ARP filter"); + return ret; + } + + /* Configure filtering of IPv6 NA frames */ + os_snprintf(path, sizeof(path), + "/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na", + bss->ifname); + ret = nl80211_write_to_file(path, + !!(filter_flags & + WPA_DATA_FRAME_FILTER_FLAG_NA)); + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to set unsolicited NA filter"); + return ret; + } + + return 0; +} + + +static int nl80211_get_ext_capab(void *priv, enum wpa_driver_if_type type, + const u8 **ext_capa, const u8 **ext_capa_mask, + unsigned int *ext_capa_len) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + enum nl80211_iftype nlmode; + unsigned int i; + + if (!ext_capa || !ext_capa_mask || !ext_capa_len) + return -1; + + nlmode = wpa_driver_nl80211_if_type(type); + + /* By default, use the per-radio values */ + *ext_capa = drv->extended_capa; + *ext_capa_mask = drv->extended_capa_mask; + *ext_capa_len = drv->extended_capa_len; + + /* Replace the default value if a per-interface type value exists */ + for (i = 0; i < drv->num_iface_ext_capa; i++) { + if (nlmode == drv->iface_ext_capa[i].iftype) { + *ext_capa = drv->iface_ext_capa[i].ext_capa; + *ext_capa_mask = drv->iface_ext_capa[i].ext_capa_mask; + *ext_capa_len = drv->iface_ext_capa[i].ext_capa_len; + break; + } + } + + return 0; +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -8710,6 +9444,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .sched_scan = wpa_driver_nl80211_sched_scan, .stop_sched_scan = wpa_driver_nl80211_stop_sched_scan, .get_scan_results2 = wpa_driver_nl80211_get_scan_results, + .abort_scan = wpa_driver_nl80211_abort_scan, .deauthenticate = driver_nl80211_deauthenticate, .authenticate = driver_nl80211_authenticate, .associate = wpa_driver_nl80211_associate, @@ -8793,7 +9528,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .vendor_cmd = nl80211_vendor_cmd, .set_qos_map = nl80211_set_qos_map, .set_wowlan = nl80211_set_wowlan, - .roaming = nl80211_roaming, .set_mac_addr = nl80211_set_mac_addr, #ifdef CONFIG_MESH .init_mesh = wpa_driver_nl80211_init_mesh, @@ -8806,8 +9540,17 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .br_set_net_param = wpa_driver_br_set_net_param, .add_tx_ts = nl80211_add_ts, .del_tx_ts = nl80211_del_ts, + .get_ifindex = nl80211_get_ifindex, +#ifdef CONFIG_DRIVER_NL80211_QCA + .roaming = nl80211_roaming, .do_acs = wpa_driver_do_acs, .set_band = nl80211_set_band, .get_pref_freq_list = nl80211_get_pref_freq_list, .set_prob_oper_freq = nl80211_set_prob_oper_freq, + .p2p_lo_start = nl80211_p2p_lo_start, + .p2p_lo_stop = nl80211_p2p_lo_stop, + .set_default_scan_ies = nl80211_set_default_scan_ies, +#endif /* CONFIG_DRIVER_NL80211_QCA */ + .configure_data_frame_filters = nl80211_configure_data_frame_filters, + .get_ext_capab = nl80211_get_ext_capab, }; diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index 5c21e0faf55cd..d0ec48c9f9734 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -25,6 +25,7 @@ #endif /* CONFIG_LIBNL20 */ struct nl80211_global { + void *ctx; struct dl_list interfaces; int if_add_ifindex; u64 if_add_wdevid; @@ -84,6 +85,7 @@ struct wpa_driver_nl80211_data { struct dl_list list; struct dl_list wiphy_list; char phyname[32]; + unsigned int wiphy_idx; u8 perm_addr[ETH_ALEN]; void *ctx; int ifindex; @@ -94,6 +96,13 @@ struct wpa_driver_nl80211_data { struct wpa_driver_capa capa; u8 *extended_capa, *extended_capa_mask; unsigned int extended_capa_len; + struct drv_nl80211_ext_capa { + enum nl80211_iftype iftype; + u8 *ext_capa, *ext_capa_mask; + unsigned int ext_capa_len; + } iface_ext_capa[NL80211_IFTYPE_MAX]; + unsigned int num_iface_ext_capa; + int has_capability; int operstate; @@ -148,9 +157,16 @@ struct wpa_driver_nl80211_data { unsigned int setband_vendor_cmd_avail:1; unsigned int get_pref_freq_list:1; unsigned int set_prob_oper_freq:1; + unsigned int scan_vendor_cmd_avail:1; + unsigned int connect_reassoc:1; + unsigned int set_wifi_conf_vendor_cmd_avail:1; + u64 vendor_scan_cookie; u64 remain_on_chan_cookie; u64 send_action_cookie; +#define MAX_SEND_ACTION_COOKIES 20 + u64 send_action_cookies[MAX_SEND_ACTION_COOKIES]; + unsigned int num_send_action_cookies; unsigned int last_mgmt_freq; @@ -166,7 +182,10 @@ struct wpa_driver_nl80211_data { struct nl_handle *rtnl_sk; /* nl_sock for NETLINK_ROUTE */ int default_if_indices[16]; + /* the AP/AP_VLAN iface that is in this bridge */ + int default_if_indices_reason[16]; int *if_indices; + int *if_indices_reason; int num_if_indices; /* From failed authentication command */ @@ -182,6 +201,13 @@ struct wpa_driver_nl80211_data { int auth_wep_tx_keyidx; int auth_local_state_change; int auth_p2p; + + /* + * Tells whether the last scan issued from wpa_supplicant was a normal + * scan (NL80211_CMD_TRIGGER_SCAN) or a vendor scan + * (NL80211_CMD_VENDOR). 0 if no pending scan request. + */ + int last_scan_cmd; }; struct nl_msg; @@ -233,6 +259,8 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags); int process_global_event(struct nl_msg *msg, void *arg); int process_bss_event(struct nl_msg *msg, void *arg); +const char * nl80211_iftype_str(enum nl80211_iftype mode); + #ifdef ANDROID int android_nl_socket_set_nonblocking(struct nl_handle *handle); int android_pno_start(struct i802_bss *bss, @@ -267,11 +295,13 @@ void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx); int wpa_driver_nl80211_scan(struct i802_bss *bss, struct wpa_driver_scan_params *params); int wpa_driver_nl80211_sched_scan(void *priv, - struct wpa_driver_scan_params *params, - u32 interval); + struct wpa_driver_scan_params *params); int wpa_driver_nl80211_stop_sched_scan(void *priv); struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv); void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv); -const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie); +int wpa_driver_nl80211_abort_scan(void *priv); +int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, + struct wpa_driver_scan_params *params); +int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len); #endif /* DRIVER_NL80211_H */ diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 4cf31238aeb70..6adc3f6d33dcd 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -66,7 +66,6 @@ struct wiphy_info_data { unsigned int device_ap_sme:1; unsigned int poll_command_supported:1; unsigned int data_tx_status:1; - unsigned int monitor_supported:1; unsigned int auth_supported:1; unsigned int connect_supported:1; unsigned int p2p_go_supported:1; @@ -129,9 +128,6 @@ static void wiphy_info_supported_iftypes(struct wiphy_info_data *info, case NL80211_IFTYPE_P2P_CLIENT: info->p2p_client_supported = 1; break; - case NL80211_IFTYPE_MONITOR: - info->monitor_supported = 1; - break; } } } @@ -352,13 +348,20 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, struct nlattr *tb) { struct wpa_driver_capa *capa = info->capa; + u8 *ext_features; + int len; if (tb == NULL) return; - if (ext_feature_isset(nla_data(tb), nla_len(tb), - NL80211_EXT_FEATURE_VHT_IBSS)) + ext_features = nla_data(tb); + len = nla_len(tb); + + if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_VHT_IBSS)) capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS; + + if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_RRM)) + capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_RRM; } @@ -428,6 +431,9 @@ static void wiphy_info_feature_flags(struct wiphy_info_data *info, if (flags & NL80211_FEATURE_HT_IBSS) capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS; + + if (flags & NL80211_FEATURE_FULL_AP_CLIENT_STATE) + capa->flags |= WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE; } @@ -476,6 +482,74 @@ static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa, } +static void wiphy_info_extended_capab(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb) +{ + int rem = 0, i; + struct nlattr *tb1[NL80211_ATTR_MAX + 1], *attr; + + if (!tb || drv->num_iface_ext_capa == NL80211_IFTYPE_MAX) + return; + + nla_for_each_nested(attr, tb, rem) { + unsigned int len; + struct drv_nl80211_ext_capa *capa; + + nla_parse(tb1, NL80211_ATTR_MAX, nla_data(attr), + nla_len(attr), NULL); + + if (!tb1[NL80211_ATTR_IFTYPE] || + !tb1[NL80211_ATTR_EXT_CAPA] || + !tb1[NL80211_ATTR_EXT_CAPA_MASK]) + continue; + + capa = &drv->iface_ext_capa[drv->num_iface_ext_capa]; + capa->iftype = nla_get_u32(tb1[NL80211_ATTR_IFTYPE]); + wpa_printf(MSG_DEBUG, + "nl80211: Driver-advertised extended capabilities for interface type %s", + nl80211_iftype_str(capa->iftype)); + + len = nla_len(tb1[NL80211_ATTR_EXT_CAPA]); + capa->ext_capa = os_malloc(len); + if (!capa->ext_capa) + goto err; + + os_memcpy(capa->ext_capa, nla_data(tb1[NL80211_ATTR_EXT_CAPA]), + len); + capa->ext_capa_len = len; + wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities", + capa->ext_capa, capa->ext_capa_len); + + len = nla_len(tb1[NL80211_ATTR_EXT_CAPA_MASK]); + capa->ext_capa_mask = os_malloc(len); + if (!capa->ext_capa_mask) + goto err; + + os_memcpy(capa->ext_capa_mask, + nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]), len); + wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask", + capa->ext_capa_mask, capa->ext_capa_len); + + drv->num_iface_ext_capa++; + if (drv->num_iface_ext_capa == NL80211_IFTYPE_MAX) + break; + } + + return; + +err: + /* Cleanup allocated memory on error */ + for (i = 0; i < NL80211_IFTYPE_MAX; i++) { + os_free(drv->iface_ext_capa[i].ext_capa); + drv->iface_ext_capa[i].ext_capa = NULL; + os_free(drv->iface_ext_capa[i].ext_capa_mask); + drv->iface_ext_capa[i].ext_capa_mask = NULL; + drv->iface_ext_capa[i].ext_capa_len = 0; + } + drv->num_iface_ext_capa = 0; +} + + static int wiphy_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -487,6 +561,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); + if (tb[NL80211_ATTR_WIPHY]) + drv->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]); + if (tb[NL80211_ATTR_WIPHY_NAME]) os_strlcpy(drv->phyname, nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]), @@ -499,6 +576,19 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) capa->max_sched_scan_ssids = nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); + if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS] && + tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL] && + tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]) { + capa->max_sched_scan_plans = + nla_get_u32(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS]); + + capa->max_sched_scan_plan_interval = + nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL]); + + capa->max_sched_scan_plan_iterations = + nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]); + } + if (tb[NL80211_ATTR_MAX_MATCH_SETS]) capa->max_match_sets = nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]); @@ -550,6 +640,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) nla_len(tb[NL80211_ATTR_EXT_CAPA])); drv->extended_capa_len = nla_len(tb[NL80211_ATTR_EXT_CAPA]); + wpa_hexdump(MSG_DEBUG, + "nl80211: Driver-advertised extended capabilities (default)", + drv->extended_capa, drv->extended_capa_len); } drv->extended_capa_mask = os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK])); @@ -557,6 +650,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) os_memcpy(drv->extended_capa_mask, nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]), nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK])); + wpa_hexdump(MSG_DEBUG, + "nl80211: Driver-advertised extended capabilities mask (default)", + drv->extended_capa_mask, + drv->extended_capa_len); } else { os_free(drv->extended_capa); drv->extended_capa = NULL; @@ -564,6 +661,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) } } + wiphy_info_extended_capab(drv, tb[NL80211_ATTR_IFTYPE_EXT_CAPA]); + if (tb[NL80211_ATTR_VENDOR_DATA]) { struct nlattr *nl; int rem; @@ -580,6 +679,7 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) case QCA_NL80211_VENDOR_SUBCMD_TEST: drv->vendor_cmd_test_avail = 1; break; +#ifdef CONFIG_DRIVER_NL80211_QCA case QCA_NL80211_VENDOR_SUBCMD_ROAMING: drv->roaming_vendor_cmd_avail = 1; break; @@ -602,6 +702,13 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) case QCA_NL80211_VENDOR_SUBCMD_SETBAND: drv->setband_vendor_cmd_avail = 1; break; + case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN: + drv->scan_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION: + drv->set_wifi_conf_vendor_cmd_avail = 1; + break; +#endif /* CONFIG_DRIVER_NL80211_QCA */ } } @@ -633,6 +740,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) capa->max_stations = nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]); + if (tb[NL80211_ATTR_MAX_CSA_COUNTERS]) + capa->max_csa_counters = + nla_get_u8(tb[NL80211_ATTR_MAX_CSA_COUNTERS]); + return NL_SKIP; } @@ -689,8 +800,6 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, if (!drv->capa.max_remain_on_chan) drv->capa.max_remain_on_chan = 5000; - if (info->channel_switch_supported) - drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA; drv->capa.wmm_ac_supported = info->wmm_ac_supported; drv->capa.mac_addr_rand_sched_scan_supported = @@ -698,10 +807,24 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, drv->capa.mac_addr_rand_scan_supported = info->mac_addr_rand_scan_supported; + if (info->channel_switch_supported) { + drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA; + if (!drv->capa.max_csa_counters) + drv->capa.max_csa_counters = 1; + } + + if (!drv->capa.max_sched_scan_plans) { + drv->capa.max_sched_scan_plans = 1; + drv->capa.max_sched_scan_plan_interval = UINT32_MAX; + drv->capa.max_sched_scan_plan_iterations = 0; + } + return 0; } +#ifdef CONFIG_DRIVER_NL80211_QCA + static int dfs_info_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -780,8 +903,12 @@ static int features_info_handler(struct nl_msg *msg, void *arg) attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS]; if (attr) { - info->flags = nla_data(attr); - info->flags_len = nla_len(attr); + int len = nla_len(attr); + info->flags = os_malloc(len); + if (info->flags != NULL) { + os_memcpy(info->flags, nla_data(attr), len); + info->flags_len = len; + } } attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA]; if (attr) @@ -840,8 +967,17 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv) if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info)) drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY; + + if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS, + &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS; + if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD, &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD; + os_free(info.flags); } +#endif /* CONFIG_DRIVER_NL80211_QCA */ + int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) { @@ -898,21 +1034,8 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) * If poll command and tx status are supported, mac80211 is new enough * to have everything we need to not need monitor interfaces. */ - drv->use_monitor = !info.poll_command_supported || !info.data_tx_status; - - if (drv->device_ap_sme && drv->use_monitor) { - /* - * Non-mac80211 drivers may not support monitor interface. - * Make sure we do not get stuck with incorrect capability here - * by explicitly testing this. - */ - if (!info.monitor_supported) { - wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor " - "with device_ap_sme since no monitor mode " - "support detected"); - drv->use_monitor = 0; - } - } + drv->use_monitor = !info.device_ap_sme && + (!info.poll_command_supported || !info.data_tx_status); /* * If we aren't going to use monitor interfaces, but the @@ -922,9 +1045,21 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) if (!drv->use_monitor && !info.data_tx_status) drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; +#ifdef CONFIG_DRIVER_NL80211_QCA qca_nl80211_check_dfs_capa(drv); qca_nl80211_get_features(drv); + /* + * To enable offchannel simultaneous support in wpa_supplicant, the + * underlying driver needs to support the same along with offchannel TX. + * Offchannel TX support is needed since remain_on_channel and + * action_tx use some common data structures and hence cannot be + * scheduled simultaneously. + */ + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX)) + drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS; +#endif /* CONFIG_DRIVER_NL80211_QCA */ + return 0; } @@ -933,6 +1068,7 @@ struct phy_info_arg { u16 *num_modes; struct hostapd_hw_modes *modes; int last_mode, last_chan_idx; + int failed; }; static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa, @@ -1050,7 +1186,7 @@ static int phy_info_freqs(struct phy_info_arg *phy_info, mode->num_channels + new_channels, sizeof(struct hostapd_channel_data)); if (!channel) - return NL_SKIP; + return NL_STOP; mode->channels = channel; mode->num_channels += new_channels; @@ -1096,7 +1232,7 @@ static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb) mode->rates = os_calloc(mode->num_rates, sizeof(int)); if (!mode->rates) - return NL_SKIP; + return NL_STOP; idx = 0; @@ -1125,8 +1261,10 @@ static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band) mode = os_realloc_array(phy_info->modes, *phy_info->num_modes + 1, sizeof(*mode)); - if (!mode) - return NL_SKIP; + if (!mode) { + phy_info->failed = 1; + return NL_STOP; + } phy_info->modes = mode; mode = &phy_info->modes[*(phy_info->num_modes)]; @@ -1162,11 +1300,12 @@ static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band) phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA], tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]); ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]); - if (ret != NL_OK) - return ret; - ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]); - if (ret != NL_OK) + if (ret == NL_OK) + ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]); + if (ret != NL_OK) { + phy_info->failed = 1; return ret; + } return NL_OK; } @@ -1381,7 +1520,7 @@ static void nl80211_reg_rule_sec(struct nlattr *tb[], static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start, - int end) + int end, int max_bw) { int c; @@ -1398,6 +1537,32 @@ static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start, if (chan->freq - 70 >= start && chan->freq + 10 <= end) chan->flag |= HOSTAPD_CHAN_VHT_70_10; + + if (max_bw >= 160) { + if (chan->freq - 10 >= start && chan->freq + 150 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_10_150; + + if (chan->freq - 30 >= start && chan->freq + 130 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_30_130; + + if (chan->freq - 50 >= start && chan->freq + 110 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_50_110; + + if (chan->freq - 70 >= start && chan->freq + 90 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_70_90; + + if (chan->freq - 90 >= start && chan->freq + 70 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_90_70; + + if (chan->freq - 110 >= start && chan->freq + 50 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_110_50; + + if (chan->freq - 130 >= start && chan->freq + 30 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_130_30; + + if (chan->freq - 150 >= start && chan->freq + 10 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_150_10; + } } } @@ -1428,7 +1593,7 @@ static void nl80211_reg_rule_vht(struct nlattr *tb[], if (!results->modes[m].vht_capab) continue; - nl80211_set_vht_mode(&results->modes[m], start, end); + nl80211_set_vht_mode(&results->modes[m], start, end, max_bw); } } @@ -1566,6 +1731,7 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) .num_modes = num_modes, .modes = NULL, .last_mode = -1, + .failed = 0, }; *num_modes = 0; @@ -1582,6 +1748,16 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) { nl80211_set_regulatory_flags(drv, &result); + if (result.failed) { + int i; + + for (i = 0; result.modes && i < *num_modes; i++) { + os_free(result.modes[i].channels); + os_free(result.modes[i].rates); + } + os_free(result.modes); + return NULL; + } return wpa_driver_nl80211_postprocess_modes(result.modes, num_modes); } diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 7b0f721e65842..762e3acc28079 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -265,10 +265,12 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, enum nl80211_commands cmd, struct nlattr *status, struct nlattr *addr, struct nlattr *req_ie, struct nlattr *resp_ie, + struct nlattr *timed_out, struct nlattr *authorized, struct nlattr *key_replay_ctr, struct nlattr *ptk_kck, - struct nlattr *ptk_kek) + struct nlattr *ptk_kek, + struct nlattr *subnet_status) { union wpa_event_data event; const u8 *ssid; @@ -284,6 +286,8 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, return; } + drv->connect_reassoc = 0; + status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS; if (cmd == NL80211_CMD_CONNECT) { @@ -319,6 +323,7 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, event.assoc_reject.resp_ies_len = nla_len(resp_ie); } event.assoc_reject.status_code = status_code; + event.assoc_reject.timed_out = timed_out != NULL; wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); return; } @@ -334,9 +339,9 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, event.assoc_info.req_ies_len = nla_len(req_ie); if (cmd == NL80211_CMD_ROAM) { - ssid = nl80211_get_ie(event.assoc_info.req_ies, - event.assoc_info.req_ies_len, - WLAN_EID_SSID); + ssid = get_ie(event.assoc_info.req_ies, + event.assoc_info.req_ies_len, + WLAN_EID_SSID); if (ssid && ssid[1] > 0 && ssid[1] <= 32) { drv->ssid_len = ssid[1]; os_memcpy(drv->ssid, ssid + 2, ssid[1]); @@ -367,6 +372,17 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, event.assoc_info.ptk_kek_len = nla_len(ptk_kek); } + if (subnet_status) { + /* + * At least for now, this is only available from + * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS and that + * attribute has the same values 0, 1, 2 as are used in the + * variable here, so no mapping between different values are + * needed. + */ + event.assoc_info.subnet_status = nla_get_u8(subnet_status); + } + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } @@ -560,9 +576,10 @@ static void mlme_event_mgmt(struct i802_bss *bss, rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq; } wpa_printf(MSG_DEBUG, - "nl80211: RX frame sa=" MACSTR + "nl80211: RX frame da=" MACSTR " sa=" MACSTR " bssid=" MACSTR " freq=%d ssi_signal=%d fc=0x%x seq_ctrl=0x%x stype=%u (%s) len=%u", - MAC2STR(mgmt->sa), rx_freq, ssi_signal, fc, + MAC2STR(mgmt->da), MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid), + rx_freq, ssi_signal, fc, le_to_host16(mgmt->seq_ctrl), stype, fc2str(fc), (unsigned int) len); event.rx_mgmt.frame = frame; @@ -639,10 +656,39 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, * Avoid issues with some roaming cases where * disconnection event for the old AP may show up after * we have started connection with the new AP. + * In case of locally generated event clear + * ignore_next_local_deauth as well, to avoid next local + * deauth event be wrongly ignored. */ - wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR, - MAC2STR(bssid), - MAC2STR(drv->auth_attempt_bssid)); + if (!os_memcmp(mgmt->sa, drv->first_bss->addr, + ETH_ALEN)) { + wpa_printf(MSG_DEBUG, + "nl80211: Received a locally generated deauth event. Clear ignore_next_local_deauth flag"); + drv->ignore_next_local_deauth = 0; + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR, + MAC2STR(bssid), + MAC2STR(drv->auth_attempt_bssid)); + } + return; + } + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && + drv->connect_reassoc && drv->associated && + os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0 && + os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0) { + /* + * Avoid issues with some roaming cases where + * disconnection event for the old AP may show up after + * we have started connection with the new AP. + */ + wpa_printf(MSG_DEBUG, + "nl80211: Ignore deauth/disassoc event from old AP " + MACSTR + " when already connecting with " MACSTR, + MAC2STR(bssid), + MAC2STR(drv->auth_attempt_bssid)); return; } @@ -679,13 +725,15 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, mgmt->u.disassoc.variable; } } else { + event.deauth_info.locally_generated = + !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); if (drv->ignore_deauth_event) { wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth"); drv->ignore_deauth_event = 0; + if (event.deauth_info.locally_generated) + drv->ignore_next_local_deauth = 0; return; } - event.deauth_info.locally_generated = - !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); if (drv->ignore_next_local_deauth) { drv->ignore_next_local_deauth = 0; if (event.deauth_info.locally_generated) { @@ -868,6 +916,7 @@ static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv, struct nlattr *tb[]) { unsigned int freq; + union wpa_event_data event; if (tb[NL80211_ATTR_MAC] == NULL) { wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined " @@ -887,7 +936,10 @@ static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv, drv->first_bss->freq = freq; } - wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + os_memset(&event, 0, sizeof(event)); + event.assoc_info.freq = freq; + + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } @@ -968,7 +1020,7 @@ static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv, static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, - struct nlattr *tb[]) + struct nlattr *tb[], int external_scan) { union wpa_event_data event; struct nlattr *nl; @@ -978,7 +1030,7 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, int freqs[MAX_REPORT_FREQS]; int num_freqs = 0; - if (drv->scan_for_auth) { + if (!external_scan && drv->scan_for_auth) { drv->scan_for_auth = 0; wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing " "cfg80211 BSS entry"); @@ -989,6 +1041,8 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, os_memset(&event, 0, sizeof(event)); info = &event.scan_info; info->aborted = aborted; + info->external_scan = external_scan; + info->nl_scan_event = 1; if (tb[NL80211_ATTR_SCAN_SSIDS]) { nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) { @@ -1004,7 +1058,7 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, } } if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) { - char msg[200], *pos, *end; + char msg[300], *pos, *end; int res; pos = msg; @@ -1109,7 +1163,7 @@ static void nl80211_new_peer_candidate(struct wpa_driver_nl80211_data *drv, return; addr = nla_data(tb[NL80211_ATTR_MAC]); - wpa_printf(MSG_DEBUG, "nl80211: New peer candidate" MACSTR, + wpa_printf(MSG_DEBUG, "nl80211: New peer candidate " MACSTR, MAC2STR(addr)); os_memset(&data, 0, sizeof(data)); @@ -1154,6 +1208,7 @@ static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv, static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv, + struct i802_bss *bss, struct nlattr **tb) { u8 *addr; @@ -1166,7 +1221,7 @@ static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv, MAC2STR(addr)); if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { - drv_event_disassoc(drv->ctx, addr); + drv_event_disassoc(bss->ctx, addr); return; } @@ -1175,7 +1230,7 @@ static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv, os_memset(&data, 0, sizeof(data)); os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN); - wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data); + wpa_supplicant_event(bss->ctx, EVENT_IBSS_PEER_LOST, &data); } @@ -1444,6 +1499,8 @@ static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb, } +#ifdef CONFIG_DRIVER_NL80211_QCA + static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv, const u8 *data, size_t len) { @@ -1593,10 +1650,12 @@ static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv, tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE], + NULL, tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK], - tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK]); + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS]); } @@ -1686,6 +1745,165 @@ static void qca_nl80211_dfs_offload_radar_event( } +static void qca_nl80211_scan_trigger_event(struct wpa_driver_nl80211_data *drv, + u8 *data, size_t len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; + u64 cookie = 0; + union wpa_event_data event; + struct scan_info *info; + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, + (struct nlattr *) data, len, NULL) || + !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]) + return; + + cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]); + if (cookie != drv->vendor_scan_cookie) { + /* External scan trigger event, ignore */ + return; + } + + /* Cookie match, own scan */ + os_memset(&event, 0, sizeof(event)); + info = &event.scan_info; + info->external_scan = 0; + info->nl_scan_event = 0; + + drv->scan_state = SCAN_STARTED; + wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, &event); +} + + +static void send_vendor_scan_event(struct wpa_driver_nl80211_data *drv, + int aborted, struct nlattr *tb[], + int external_scan) +{ + union wpa_event_data event; + struct nlattr *nl; + int rem; + struct scan_info *info; + int freqs[MAX_REPORT_FREQS]; + int num_freqs = 0; + + os_memset(&event, 0, sizeof(event)); + info = &event.scan_info; + info->aborted = aborted; + info->external_scan = external_scan; + + if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) { + nla_for_each_nested(nl, + tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS], rem) { + struct wpa_driver_scan_ssid *s = + &info->ssids[info->num_ssids]; + s->ssid = nla_data(nl); + s->ssid_len = nla_len(nl); + wpa_printf(MSG_DEBUG, + "nl80211: Scan probed for SSID '%s'", + wpa_ssid_txt(s->ssid, s->ssid_len)); + info->num_ssids++; + if (info->num_ssids == WPAS_MAX_SCAN_SSIDS) + break; + } + } + + if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES]) { + char msg[300], *pos, *end; + int res; + + pos = msg; + end = pos + sizeof(msg); + *pos = '\0'; + + nla_for_each_nested(nl, + tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES], + rem) { + freqs[num_freqs] = nla_get_u32(nl); + res = os_snprintf(pos, end - pos, " %d", + freqs[num_freqs]); + if (!os_snprintf_error(end - pos, res)) + pos += res; + num_freqs++; + if (num_freqs == MAX_REPORT_FREQS - 1) + break; + } + + info->freqs = freqs; + info->num_freqs = num_freqs; + wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s", + msg); + } + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); +} + + +static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv, + u8 *data, size_t len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; + u64 cookie = 0; + enum scan_status status; + int external_scan; + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, + (struct nlattr *) data, len, NULL) || + !tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS] || + !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]) + return; + + status = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS]); + if (status >= VENDOR_SCAN_STATUS_MAX) + return; /* invalid status */ + + cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]); + if (cookie != drv->vendor_scan_cookie) { + /* Event from an external scan, get scan results */ + external_scan = 1; + } else { + external_scan = 0; + if (status == VENDOR_SCAN_STATUS_NEW_RESULTS) + drv->scan_state = SCAN_COMPLETED; + else + drv->scan_state = SCAN_ABORTED; + + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, + drv->ctx); + drv->vendor_scan_cookie = 0; + drv->last_scan_cmd = 0; + } + + send_vendor_scan_event(drv, (status == VENDOR_SCAN_STATUS_ABORTED), tb, + external_scan); +} + + +static void qca_nl80211_p2p_lo_stop_event(struct wpa_driver_nl80211_data *drv, + u8 *data, size_t len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX + 1]; + union wpa_event_data event; + + wpa_printf(MSG_DEBUG, + "nl80211: P2P listen offload stop vendor event received"); + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX, + (struct nlattr *) data, len, NULL) || + !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON]) + return; + + os_memset(&event, 0, sizeof(event)); + event.p2p_lo_stop.reason_code = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON]); + + wpa_printf(MSG_DEBUG, + "nl80211: P2P Listen offload stop reason: %d", + event.p2p_lo_stop.reason_code); + wpa_supplicant_event(drv->ctx, EVENT_P2P_LO_STOP, &event); +} + +#endif /* CONFIG_DRIVER_NL80211_QCA */ + + static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *data, size_t len) { @@ -1693,6 +1911,7 @@ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, case QCA_NL80211_VENDOR_SUBCMD_TEST: wpa_hexdump(MSG_DEBUG, "nl80211: QCA test event", data, len); break; +#ifdef CONFIG_DRIVER_NL80211_QCA case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: qca_nl80211_avoid_freq(drv, data, len); break; @@ -1709,6 +1928,16 @@ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: qca_nl80211_dfs_offload_radar_event(drv, subcmd, data, len); break; + case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN: + qca_nl80211_scan_trigger_event(drv, data, len); + break; + case QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE: + qca_nl80211_scan_done_event(drv, data, len); + break; + case QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP: + qca_nl80211_p2p_lo_stop_event(drv, data, len); + break; +#endif /* CONFIG_DRIVER_NL80211_QCA */ default: wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported QCA vendor event %u", @@ -1831,6 +2060,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, { 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", cmd, nl80211_command_to_string(cmd), bss->ifname); @@ -1883,28 +2113,38 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, case NL80211_CMD_NEW_SCAN_RESULTS: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: New scan results available"); - drv->scan_state = SCAN_COMPLETED; drv->scan_complete_events = 1; - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, - drv->ctx); - send_scan_event(drv, 0, tb); + if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { + drv->scan_state = SCAN_COMPLETED; + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, + drv, drv->ctx); + drv->last_scan_cmd = 0; + } else { + external_scan_event = 1; + } + send_scan_event(drv, 0, tb, external_scan_event); break; case NL80211_CMD_SCHED_SCAN_RESULTS: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: New sched scan results available"); drv->scan_state = SCHED_SCAN_RESULTS; - send_scan_event(drv, 0, tb); + send_scan_event(drv, 0, tb, 0); break; case NL80211_CMD_SCAN_ABORTED: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted"); - drv->scan_state = SCAN_ABORTED; - /* - * Need to indicate that scan results are available in order - * not to make wpa_supplicant stop its scanning. - */ - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, - drv->ctx); - send_scan_event(drv, 1, tb); + if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { + drv->scan_state = SCAN_ABORTED; + /* + * Need to indicate that scan results are available in + * order not to make wpa_supplicant stop its scanning. + */ + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, + drv, drv->ctx); + drv->last_scan_cmd = 0; + } else { + external_scan_event = 1; + } + send_scan_event(drv, 1, tb, external_scan_event); break; case NL80211_CMD_AUTHENTICATE: case NL80211_CMD_ASSOCIATE: @@ -1927,7 +2167,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_REQ_IE], tb[NL80211_ATTR_RESP_IE], - NULL, NULL, NULL, NULL); + tb[NL80211_ATTR_TIMED_OUT], + NULL, NULL, NULL, NULL, NULL); break; case NL80211_CMD_CH_SWITCH_NOTIFY: mlme_event_ch_switch(drv, @@ -1972,7 +2213,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, nl80211_new_station_event(drv, bss, tb); break; case NL80211_CMD_DEL_STATION: - nl80211_del_station_event(drv, tb); + nl80211_del_station_event(drv, bss, tb); break; case NL80211_CMD_SET_REKEY_OFFLOAD: nl80211_rekey_offload_event(drv, tb); diff --git a/src/drivers/driver_nl80211_monitor.c b/src/drivers/driver_nl80211_monitor.c index 45385da91f6a1..9376d1143800d 100644 --- a/src/drivers/driver_nl80211_monitor.c +++ b/src/drivers/driver_nl80211_monitor.c @@ -136,7 +136,7 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) break; case IEEE80211_RADIOTAP_TX_FLAGS: injected = 1; - failed = le_to_host16((*(uint16_t *) iter.this_arg)) & + failed = le_to_host16((*(le16 *) iter.this_arg)) & IEEE80211_RADIOTAP_F_TX_FAIL; break; case IEEE80211_RADIOTAP_DATA_RETRIES: diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index 4b762eafbe8ae..c115b6b31b7dc 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -1,5 +1,6 @@ /* * Driver interaction with Linux nl80211/cfg80211 - Scanning + * Copyright(c) 2015 Intel Deutschland GmbH * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> * Copyright (c) 2009-2010, Atheros Communications @@ -14,6 +15,8 @@ #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "common/qca-vendor.h" #include "driver_nl80211.h" @@ -93,12 +96,20 @@ static int nl80211_get_noise_for_scan_results( void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_driver_nl80211_data *drv = eloop_ctx; + + wpa_printf(MSG_DEBUG, "nl80211: Scan timeout - try to abort it"); + if (!wpa_driver_nl80211_abort_scan(drv->first_bss)) + return; + + wpa_printf(MSG_DEBUG, "nl80211: Failed to abort scan"); + if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) { wpa_driver_nl80211_set_mode(drv->first_bss, drv->ap_scan_as_station); drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; } - wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + + wpa_printf(MSG_DEBUG, "nl80211: Try to get scan results"); wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); } @@ -131,6 +142,8 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd, goto fail; } nla_nest_end(msg, ssids); + } else { + wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested"); } if (params->extra_ies) { @@ -252,6 +265,13 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss, goto fail; } + if (params->bssid) { + wpa_printf(MSG_DEBUG, "nl80211: Scan for a specific BSSID: " + MACSTR, MAC2STR(params->bssid)); + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) + goto fail; + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { @@ -297,6 +317,7 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss, eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN; fail: nlmsg_free(msg); @@ -304,16 +325,82 @@ fail: } +static int +nl80211_sched_scan_add_scan_plans(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, + struct wpa_driver_scan_params *params) +{ + struct nlattr *plans; + struct sched_scan_plan *scan_plans = params->sched_scan_plans; + unsigned int i; + + plans = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_PLANS); + if (!plans) + return -1; + + for (i = 0; i < params->sched_scan_plans_num; i++) { + struct nlattr *plan = nla_nest_start(msg, i + 1); + + if (!plan) + return -1; + + if (!scan_plans[i].interval || + scan_plans[i].interval > + drv->capa.max_sched_scan_plan_interval) { + wpa_printf(MSG_DEBUG, + "nl80211: sched scan plan no. %u: Invalid interval: %u", + i, scan_plans[i].interval); + return -1; + } + + if (nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL, + scan_plans[i].interval)) + return -1; + + if (scan_plans[i].iterations > + drv->capa.max_sched_scan_plan_iterations) { + wpa_printf(MSG_DEBUG, + "nl80211: sched scan plan no. %u: Invalid number of iterations: %u", + i, scan_plans[i].iterations); + return -1; + } + + if (scan_plans[i].iterations && + nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_ITERATIONS, + scan_plans[i].iterations)) + return -1; + + nla_nest_end(msg, plan); + + /* + * All the scan plans must specify the number of iterations + * except the last plan, which will run infinitely. So if the + * number of iterations is not specified, this ought to be the + * last scan plan. + */ + if (!scan_plans[i].iterations) + break; + } + + if (i != params->sched_scan_plans_num - 1) { + wpa_printf(MSG_DEBUG, + "nl80211: All sched scan plans but the last must specify number of iterations"); + return -1; + } + + nla_nest_end(msg, plans); + return 0; +} + + /** * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan * @priv: Pointer to private driver data from wpa_driver_nl80211_init() * @params: Scan parameters - * @interval: Interval between scan cycles in milliseconds * Returns: 0 on success, -1 on failure or if not supported */ int wpa_driver_nl80211_sched_scan(void *priv, - struct wpa_driver_scan_params *params, - u32 interval) + struct wpa_driver_scan_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; @@ -328,11 +415,27 @@ int wpa_driver_nl80211_sched_scan(void *priv, return android_pno_start(bss, params); #endif /* ANDROID */ + if (!params->sched_scan_plans_num || + params->sched_scan_plans_num > drv->capa.max_sched_scan_plans) { + wpa_printf(MSG_ERROR, + "nl80211: Invalid number of sched scan plans: %u", + params->sched_scan_plans_num); + return -1; + } + msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params); - if (!msg || - nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval)) + if (!msg) goto fail; + if (drv->capa.max_sched_scan_plan_iterations) { + if (nl80211_sched_scan_add_scan_plans(drv, msg, params)) + goto fail; + } else { + if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, + params->sched_scan_plans[0].interval * 1000)) + goto fail; + } + if ((drv->num_filter_ssids && (int) drv->num_filter_ssids <= drv->capa.max_match_sets) || params->filter_rssi) { @@ -395,8 +498,7 @@ int wpa_driver_nl80211_sched_scan(void *priv, goto fail; } - wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - " - "scan interval %d msec", ret, interval); + wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d)", ret); fail: nlmsg_free(msg); @@ -436,28 +538,6 @@ int wpa_driver_nl80211_stop_sched_scan(void *priv) } -const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) -{ - const u8 *end, *pos; - - if (ies == NULL) - return NULL; - - pos = ies; - end = ies + ies_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; -} - - static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, const u8 *ie, size_t ie_len) { @@ -467,7 +547,7 @@ static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, if (drv->filter_ssids == NULL) return 0; - ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID); + ssid = get_ie(ie, ie_len, WLAN_EID_SSID); if (ssid == NULL) return 1; @@ -628,9 +708,9 @@ int bss_info_handler(struct nl_msg *msg, void *arg) if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0) continue; - s1 = nl80211_get_ie((u8 *) (res->res[i] + 1), - res->res[i]->ie_len, WLAN_EID_SSID); - s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID); + s1 = get_ie((u8 *) (res->res[i] + 1), + res->res[i]->ie_len, WLAN_EID_SSID); + s2 = get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID); if (s1 == NULL || s2 == NULL || s1[1] != s2[1] || os_memcmp(s1, s2, 2 + s1[1]) != 0) continue; @@ -781,3 +861,263 @@ void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv) wpa_scan_results_free(res); } + + +int wpa_driver_nl80211_abort_scan(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret; + struct nl_msg *msg; + + wpa_printf(MSG_DEBUG, "nl80211: Abort scan"); + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)", + ret, strerror(-ret)); + } + + return ret; +} + + +#ifdef CONFIG_DRIVER_NL80211_QCA + +static int scan_cookie_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + u64 *cookie = 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_vendor = tb[NL80211_ATTR_VENDOR_DATA]; + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1]; + + nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_SCAN_MAX, + nla_data(nl_vendor), nla_len(nl_vendor), NULL); + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]) + *cookie = nla_get_u64( + tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]); + } + + return NL_SKIP; +} + + +/** + * wpa_driver_nl80211_vendor_scan - Request the driver to initiate a vendor scan + * @bss: Pointer to private driver data from wpa_driver_nl80211_init() + * @params: Scan parameters + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, + struct wpa_driver_scan_params *params) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg = NULL; + struct nlattr *attr; + size_t i; + u32 scan_flags = 0; + int ret = -1; + u64 cookie = 0; + + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: vendor scan request"); + drv->scan_for_auth = 0; + + 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_TRIGGER_SCAN) ) + goto fail; + + attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (attr == NULL) + goto fail; + + if (params->num_ssids) { + struct nlattr *ssids; + + ssids = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS); + 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); + if (nla_put(msg, i + 1, params->ssids[i].ssid_len, + params->ssids[i].ssid)) + goto fail; + } + nla_nest_end(msg, ssids); + } + + if (params->extra_ies) { + wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs", + params->extra_ies, params->extra_ies_len); + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_IE, + params->extra_ies_len, params->extra_ies)) + goto fail; + } + + if (params->freqs) { + struct nlattr *freqs; + + freqs = nla_nest_start(msg, + QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES); + if (freqs == NULL) + goto fail; + for (i = 0; params->freqs[i]; i++) { + wpa_printf(MSG_MSGDUMP, + "nl80211: Scan frequency %u MHz", + params->freqs[i]); + if (nla_put_u32(msg, i + 1, params->freqs[i])) + goto fail; + } + nla_nest_end(msg, freqs); + } + + os_free(drv->filter_ssids); + drv->filter_ssids = params->filter_ssids; + params->filter_ssids = NULL; + drv->num_filter_ssids = params->num_filter_ssids; + + if (params->low_priority && drv->have_low_prio_scan) { + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY"); + scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY; + } + + if (params->mac_addr_rand) { + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR"); + scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR; + + if (params->mac_addr) { + wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR, + MAC2STR(params->mac_addr)); + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC, + ETH_ALEN, params->mac_addr)) + goto fail; + } + + if (params->mac_addr_mask) { + wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: " + MACSTR, MAC2STR(params->mac_addr_mask)); + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK, + ETH_ALEN, params->mac_addr_mask)) + goto fail; + } + } + + if (scan_flags && + nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags)) + goto fail; + + if (params->p2p_probe) { + struct nlattr *rates; + + wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates"); + + rates = nla_nest_start(msg, + QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES); + if (rates == NULL) + goto fail; + + /* + * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates + * by masking out everything else apart from the OFDM rates 6, + * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz + * rates are left enabled. + */ + if (nla_put(msg, NL80211_BAND_2GHZ, 8, + "\x0c\x12\x18\x24\x30\x48\x60\x6c")) + goto fail; + nla_nest_end(msg, rates); + + if (nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE)) + goto fail; + } + + nla_nest_end(msg, attr); + + ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Vendor scan trigger failed: ret=%d (%s)", + ret, strerror(-ret)); + goto fail; + } + + drv->vendor_scan_cookie = cookie; + drv->scan_state = SCAN_REQUESTED; + + wpa_printf(MSG_DEBUG, + "nl80211: Vendor scan requested (ret=%d) - scan timeout 30 seconds, scan cookie:0x%llx", + ret, (long long unsigned int) cookie); + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + eloop_register_timeout(30, 0, wpa_driver_nl80211_scan_timeout, + drv, drv->ctx); + drv->last_scan_cmd = NL80211_CMD_VENDOR; + +fail: + nlmsg_free(msg); + return ret; +} + + +/** + * nl80211_set_default_scan_ies - Set the scan default IEs to the driver + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * @ies: Pointer to IEs buffer + * @ies_len: Length of IEs in bytes + * Returns: 0 on success, -1 on failure + */ +int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg = NULL; + struct nlattr *attr; + int ret = -1; + + if (!drv->set_wifi_conf_vendor_cmd_avail) + return -1; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION)) + goto fail; + + attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (attr == NULL) + goto fail; + + wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan default IEs", ies, ies_len); + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES, + ies_len, ies)) + goto fail; + + nla_nest_end(msg, attr); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Set scan default IEs failed: ret=%d (%s)", + ret, strerror(-ret)); + goto fail; + } + +fail: + nlmsg_free(msg); + return ret; +} + +#endif /* CONFIG_DRIVER_NL80211_QCA */ diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c index 1f1676a20ac58..43d41937d474c 100644 --- a/src/drivers/driver_privsep.c +++ b/src/drivers/driver_privsep.c @@ -161,11 +161,11 @@ wpa_driver_privsep_get_scan_results2(void *priv) return NULL; } - while (results->num < (size_t) num && pos + sizeof(int) < end) { + while (results->num < (size_t) num && end - pos > (int) sizeof(int)) { int len; os_memcpy(&len, pos, sizeof(int)); pos += sizeof(int); - if (len < 0 || len > 10000 || pos + len > end) + if (len < 0 || len > 10000 || len > end - pos) break; r = os_malloc(len); diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c index d3e05955f7a88..e8a51354dd0cc 100644 --- a/src/drivers/driver_roboswitch.c +++ b/src/drivers/driver_roboswitch.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - roboswitch driver interface - * Copyright (c) 2008-2009 Jouke Witteveen + * Copyright (c) 2008-2012 Jouke Witteveen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -401,7 +401,9 @@ static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname) os_free(drv); return NULL; } - if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR) { + /* BCM63xx devices provide 0 here */ + if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR && + if_mii(&drv->ifr)->phy_id != 0) { wpa_printf(MSG_INFO, "%s: Invalid phy address (not a " "RoboSwitch?)", __func__); os_free(drv); diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index 01defdff4f155..791cd5d435d00 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -422,7 +422,7 @@ static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv) static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, - char *data, int len) + char *data, unsigned int len) { struct iw_event iwe_buf, *iwe = &iwe_buf; char *pos, *end, *custom, *buf; @@ -430,13 +430,13 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, pos = data; end = data + len; - while (pos + IW_EV_LCP_LEN <= end) { + while ((size_t) (end - pos) >= IW_EV_LCP_LEN) { /* Event data may be unaligned, so make a local, aligned copy * before processing. */ os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", iwe->cmd, iwe->len); - if (iwe->len <= IW_EV_LCP_LEN) + if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos) return; custom = pos + IW_EV_POINT_LEN; @@ -480,7 +480,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, } break; case IWEVMICHAELMICFAILURE: - if (custom + iwe->u.data.length > end) { + if (iwe->u.data.length > end - custom) { wpa_printf(MSG_DEBUG, "WEXT: Invalid " "IWEVMICHAELMICFAILURE length"); return; @@ -489,7 +489,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, drv->ctx, custom, iwe->u.data.length); break; case IWEVCUSTOM: - if (custom + iwe->u.data.length > end) { + if (iwe->u.data.length > end - custom) { wpa_printf(MSG_DEBUG, "WEXT: Invalid " "IWEVCUSTOM length"); return; @@ -508,7 +508,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, NULL); break; case IWEVASSOCREQIE: - if (custom + iwe->u.data.length > end) { + if (iwe->u.data.length > end - custom) { wpa_printf(MSG_DEBUG, "WEXT: Invalid " "IWEVASSOCREQIE length"); return; @@ -517,7 +517,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, drv, custom, iwe->u.data.length); break; case IWEVASSOCRESPIE: - if (custom + iwe->u.data.length > end) { + if (iwe->u.data.length > end - custom) { wpa_printf(MSG_DEBUG, "WEXT: Invalid " "IWEVASSOCRESPIE length"); return; @@ -526,7 +526,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, drv, custom, iwe->u.data.length); break; case IWEVPMKIDCAND: - if (custom + iwe->u.data.length > end) { + if (iwe->u.data.length > end - custom) { wpa_printf(MSG_DEBUG, "WEXT: Invalid " "IWEVPMKIDCAND length"); return; @@ -1220,7 +1220,7 @@ static void wext_get_scan_ssid(struct iw_event *iwe, char *end) { int ssid_len = iwe->u.essid.length; - if (custom + ssid_len > end) + if (ssid_len > end - custom) return; if (iwe->u.essid.flags && ssid_len > 0 && @@ -1316,7 +1316,7 @@ static void wext_get_scan_rate(struct iw_event *iwe, size_t clen; clen = iwe->len; - if (custom + clen > end) + if (clen > (size_t) (end - custom)) return; maxrate = 0; while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) { @@ -1369,7 +1369,7 @@ static void wext_get_scan_custom(struct iw_event *iwe, u8 *tmp; clen = iwe->u.data.length; - if (custom + clen > end) + if (clen > (size_t) (end - custom)) return; if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) { @@ -1441,8 +1441,8 @@ static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res, /* Figure out whether we need to fake any IEs */ pos = data->ie; end = pos + data->ie_len; - while (pos && pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (pos && end - pos > 1) { + if (2 + pos[1] > end - pos) break; if (pos[0] == WLAN_EID_SSID) ssid_ie = pos; @@ -1530,11 +1530,11 @@ struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv) end = (char *) res_buf + len; os_memset(&data, 0, sizeof(data)); - while (pos + IW_EV_LCP_LEN <= end) { + while ((size_t) (end - pos) >= IW_EV_LCP_LEN) { /* Event data may be unaligned, so make a local, aligned copy * before processing. */ os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); - if (iwe->len <= IW_EV_LCP_LEN) + if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos) break; custom = pos + IW_EV_POINT_LEN; diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c index f95f3ccf5b66a..422a22064ebe2 100644 --- a/src/drivers/driver_wired.c +++ b/src/drivers/driver_wired.c @@ -8,12 +8,17 @@ */ #include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "driver.h" + #include <sys/ioctl.h> +#undef IFNAMSIZ #include <net/if.h> #ifdef __linux__ #include <netpacket/packet.h> #include <net/if_arp.h> -#include <net/if.h> #endif /* __linux__ */ #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) #include <net/if_dl.h> @@ -23,10 +28,6 @@ #include <sys/sockio.h> #endif /* __sun__ */ -#include "common.h" -#include "eloop.h" -#include "driver.h" - #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c index a98af9ac7d711..00773a7113f69 100644 --- a/src/drivers/drivers.c +++ b/src/drivers/drivers.c @@ -10,42 +10,6 @@ #include "utils/common.h" #include "driver.h" -#ifdef CONFIG_DRIVER_WEXT -extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */ -#endif /* CONFIG_DRIVER_WEXT */ -#ifdef CONFIG_DRIVER_NL80211 -extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */ -#endif /* CONFIG_DRIVER_NL80211 */ -#ifdef CONFIG_DRIVER_HOSTAP -extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ -#endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_BSD -extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ -#endif /* CONFIG_DRIVER_BSD */ -#ifdef CONFIG_DRIVER_OPENBSD -extern struct wpa_driver_ops wpa_driver_openbsd_ops; /* driver_openbsd.c */ -#endif /* CONFIG_DRIVER_OPENBSD */ -#ifdef CONFIG_DRIVER_NDIS -extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */ -#endif /* CONFIG_DRIVER_NDIS */ -#ifdef CONFIG_DRIVER_WIRED -extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ -#endif /* CONFIG_DRIVER_WIRED */ -#ifdef CONFIG_DRIVER_MACSEC_QCA - /* driver_macsec_qca.c */ -extern struct wpa_driver_ops wpa_driver_macsec_qca_ops; -#endif /* CONFIG_DRIVER_MACSEC_QCA */ -#ifdef CONFIG_DRIVER_ROBOSWITCH -/* driver_roboswitch.c */ -extern struct wpa_driver_ops wpa_driver_roboswitch_ops; -#endif /* CONFIG_DRIVER_ROBOSWITCH */ -#ifdef CONFIG_DRIVER_ATHEROS -extern struct wpa_driver_ops wpa_driver_atheros_ops; /* driver_atheros.c */ -#endif /* CONFIG_DRIVER_ATHEROS */ -#ifdef CONFIG_DRIVER_NONE -extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ -#endif /* CONFIG_DRIVER_NONE */ - const struct wpa_driver_ops *const wpa_drivers[] = { diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak index 3dd43c73803e7..c6d3f8181808a 100644 --- a/src/drivers/drivers.mak +++ b/src/drivers/drivers.mak @@ -29,12 +29,15 @@ DRV_OBJS += ../src/drivers/driver_nl80211_capa.o DRV_OBJS += ../src/drivers/driver_nl80211_event.o DRV_OBJS += ../src/drivers/driver_nl80211_monitor.o DRV_OBJS += ../src/drivers/driver_nl80211_scan.o -DRV_OBJS += ../src/utils/radiotap.o +ifdef CONFIG_DRIVER_NL80211_QCA +DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA +endif NEED_SME=y NEED_AP_MLME=y NEED_NETLINK=y NEED_LINUX_IOCTL=y NEED_RFKILL=y +NEED_RADIOTAP=y ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 @@ -60,7 +63,9 @@ else endif ifdef CONFIG_LIBNL20 - DRV_LIBS += -lnl-genl + ifndef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-genl + endif DRV_CFLAGS += -DCONFIG_LIBNL20 endif endif @@ -159,6 +164,10 @@ ifdef NEED_RFKILL DRV_OBJS += ../src/drivers/rfkill.o endif +ifdef NEED_RADIOTAP +DRV_OBJS += ../src/utils/radiotap.o +endif + ifdef CONFIG_VLAN_NETLINK ifdef CONFIG_FULL_DYNAMIC_VLAN ifdef CONFIG_LIBNL32 diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk index 8da4c53e0f82e..c6fe4c20c561b 100644 --- a/src/drivers/drivers.mk +++ b/src/drivers/drivers.mk @@ -25,12 +25,15 @@ DRV_OBJS += src/drivers/driver_nl80211_capa.c DRV_OBJS += src/drivers/driver_nl80211_event.c DRV_OBJS += src/drivers/driver_nl80211_monitor.c DRV_OBJS += src/drivers/driver_nl80211_scan.c -DRV_OBJS += src/utils/radiotap.c +ifdef CONFIG_DRIVER_NL80211_QCA +DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA +endif NEED_SME=y NEED_AP_MLME=y NEED_NETLINK=y NEED_LINUX_IOCTL=y NEED_RFKILL=y +NEED_RADIOTAP=y ifdef CONFIG_LIBNL32 DRV_LIBS += -lnl-3 @@ -48,7 +51,9 @@ else endif ifdef CONFIG_LIBNL20 - DRV_LIBS += -lnl-genl + ifndef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-genl + endif DRV_CFLAGS += -DCONFIG_LIBNL20 endif endif @@ -144,6 +149,10 @@ ifdef NEED_RFKILL DRV_OBJS += src/drivers/rfkill.c endif +ifdef NEED_RADIOTAP +DRV_OBJS += src/utils/radiotap.c +endif + ifdef CONFIG_DRIVER_CUSTOM DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM endif diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index ae16ba9cb1e31..2206941514345 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -10,6 +10,7 @@ * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com> * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com> * Copyright 2008 Colin McCabe <colin@cozybit.com> + * Copyright 2015 Intel Deutschland GmbH * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -321,14 +322,24 @@ * @NL80211_CMD_GET_SCAN: get scan results * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the - * probe requests at CCK rate or not. + * probe requests at CCK rate or not. %NL80211_ATTR_MAC can be used to + * specify a BSSID to scan for; if not included, the wildcard BSSID will + * be used. * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to * NL80211_CMD_GET_SCAN and on the "scan" multicast group) * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, * partial scan results may be available * * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain - * intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL. + * intervals and certain number of cycles, as specified by + * %NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is + * not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified, + * scheduled scan will run in an infinite loop with the specified interval. + * These attributes are mutually exculsive, + * i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if + * NL80211_ATTR_SCHED_SCAN_PLANS is defined. + * If for some reason scheduled scan is aborted by the driver, all scan + * plans are canceled (including scan plans that did not start yet). * Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS) * are passed, they are used in the probe requests. For * broadcast, a broadcast SSID must be passed (ie. an empty @@ -418,7 +429,11 @@ * @NL80211_CMD_ASSOCIATE: association request and notification; like * NL80211_CMD_AUTHENTICATE but for Association and Reassociation * (similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request, - * MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives). + * MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives). The + * %NL80211_ATTR_PREV_BSSID attribute is used to specify whether the + * request is for the initial association to an ESS (that attribute not + * included) or for reassociation within the ESS (that attribute is + * included). * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like * NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to * MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication @@ -468,6 +483,9 @@ * set of BSSID,frequency parameters is used (i.e., either the enforcing * %NL80211_ATTR_MAC,%NL80211_ATTR_WIPHY_FREQ or the less strict * %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT). + * %NL80211_ATTR_PREV_BSSID can be used to request a reassociation within + * the ESS in case the device is already associated and an association with + * a different BSS is desired. * Background scan period can optionally be * specified in %NL80211_ATTR_BG_SCAN_PERIOD, * if not specified default background scan configuration @@ -475,7 +493,12 @@ * This attribute is ignored if driver does not support roam scan. * It is also sent as an event, with the BSSID and response IEs when the * connection is established or failed to be established. This can be - * determined by the STATUS_CODE attribute. + * determined by the %NL80211_ATTR_STATUS_CODE attribute (0 = success, + * non-zero = failure). If %NL80211_ATTR_TIMED_OUT is included in the + * event, the connection attempt failed due to not being able to initiate + * authentication/association or not receiving a response from the AP. + * Non-zero %NL80211_ATTR_STATUS_CODE value is indicated in that case as + * well to remain backwards compatible. * @NL80211_CMD_ROAM: request that the card roam (currently not implemented), * sent as an event when the card/driver roamed by itself. * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify @@ -811,6 +834,10 @@ * as an event to indicate changes for devices with wiphy-specific regdom * management. * + * @NL80211_CMD_ABORT_SCAN: Stop an ongoing scan. Returns -ENOENT if a scan is + * not running. The driver indicates the status of the scan through + * cfg80211_scan_done(). + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -997,6 +1024,8 @@ enum nl80211_commands { NL80211_CMD_WIPHY_REG_CHANGE, + NL80211_CMD_ABORT_SCAN, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1270,8 +1299,11 @@ enum nl80211_commands { * @NL80211_ATTR_RESP_IE: (Re)association response information elements as * sent by peer, for ROAM and successful CONNECT events. * - * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE - * commands to specify using a reassociate frame + * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used in ASSOCIATE and CONNECT + * commands to specify a request to reassociate within an ESS, i.e., to use + * Reassociate Request frame (with the value of this attribute in the + * Current AP address field) instead of Association Request frame which is + * used for the initial association to an ESS. * * @NL80211_ATTR_KEY: key information in a nested attribute with * %NL80211_KEY_* sub-attributes @@ -1712,6 +1744,8 @@ enum nl80211_commands { * underlying device supports these minimal RRM features: * %NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES, * %NL80211_FEATURE_QUIET, + * Or, if global RRM is supported, see: + * %NL80211_EXT_FEATURE_RRM * If this flag is used, driver must add the Power Capabilities IE to the * association request. In addition, it must also set the RRM capability * flag in the association request's Capability Info field. @@ -1754,12 +1788,85 @@ enum nl80211_commands { * should be contained in the result as the sum of the respective counters * over all channels. * - * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before a scheduled scan (or a - * WoWLAN net-detect scan) is started, u32 in seconds. + * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a + * scheduled scan is started. Or the delay before a WoWLAN + * net-detect scan is started, counting from the moment the + * system is suspended. This value is a u32, in seconds. * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device * is operating in an indoor environment. * + * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS: maximum number of scan plans for + * scheduled scan supported by the device (u32), a wiphy attribute. + * @NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL: maximum interval (in seconds) for + * a scan plan (u32), a wiphy attribute. + * @NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS: maximum number of iterations in + * a scan plan (u32), a wiphy attribute. + * @NL80211_ATTR_SCHED_SCAN_PLANS: a list of scan plans for scheduled scan. + * Each scan plan defines the number of scan iterations and the interval + * between scans. The last scan plan will always run infinitely, + * thus it must not specify the number of iterations, only the interval + * between scans. The scan plans are executed sequentially. + * Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan. + * @NL80211_ATTR_PBSS: flag attribute. If set it means operate + * in a PBSS. Specified in %NL80211_CMD_CONNECT to request + * connecting to a PCP, and in %NL80211_CMD_START_AP to start + * a PCP instead of AP. Relevant for DMG networks only. + * @NL80211_ATTR_BSS_SELECT: nested attribute for driver supporting the + * BSS selection feature. When used with %NL80211_CMD_GET_WIPHY it contains + * attributes according &enum nl80211_bss_select_attr to indicate what + * BSS selection behaviours are supported. When used with %NL80211_CMD_CONNECT + * it contains the behaviour-specific attribute containing the parameters for + * BSS selection to be done by driver and/or firmware. + * + * @NL80211_ATTR_STA_SUPPORT_P2P_PS: whether P2P PS mechanism supported + * or not. u8, one of the values of &enum nl80211_sta_p2p_ps_status + * + * @NL80211_ATTR_PAD: attribute used for padding for 64-bit alignment + * + * @NL80211_ATTR_IFTYPE_EXT_CAPA: Nested attribute of the following attributes: + * %NL80211_ATTR_IFTYPE, %NL80211_ATTR_EXT_CAPA, + * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities per + * interface type. + * + * @NL80211_ATTR_MU_MIMO_GROUP_DATA: array of 24 bytes that defines a MU-MIMO + * groupID for monitor mode. + * The first 8 bytes are a mask that defines the membership in each + * group (there are 64 groups, group 0 and 63 are reserved), + * each bit represents a group and set to 1 for being a member in + * that group and 0 for not being a member. + * The remaining 16 bytes define the position in each group: 2 bits for + * each group. + * (smaller group numbers represented on most significant bits and bigger + * group numbers on least significant bits.) + * This attribute is used only if all interfaces are in monitor mode. + * Set this attribute in order to monitor packets using the given MU-MIMO + * groupID data. + * to turn off that feature set all the bits of the groupID to zero. + * @NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR: mac address for the sniffer to follow + * when using MU-MIMO air sniffer. + * to turn that feature off set an invalid mac address + * (e.g. FF:FF:FF:FF:FF:FF) + * + * @NL80211_ATTR_SCAN_START_TIME_TSF: The time at which the scan was actually + * started (u64). The time is the TSF of the BSS the interface that + * requested the scan is connected to (if available, otherwise this + * attribute must not be included). + * @NL80211_ATTR_SCAN_START_TIME_TSF_BSSID: The BSS according to which + * %NL80211_ATTR_SCAN_START_TIME_TSF is set. + * @NL80211_ATTR_MEASUREMENT_DURATION: measurement duration in TUs (u16). If + * %NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY is not set, this is the + * maximum measurement duration allowed. This attribute is used with + * measurement requests. It can also be used with %NL80211_CMD_TRIGGER_SCAN + * if the scan is used for beacon report radio measurement. + * @NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY: flag attribute that indicates + * that the duration specified with %NL80211_ATTR_MEASUREMENT_DURATION is + * mandatory. If this flag is not set, the duration is the maximum duration + * and the actual measurement duration may be shorter. + * + * @NL80211_ATTR_MESH_PEER_AID: Association ID for the mesh peer (u16). This is + * used to pull the stored data for mesh peer in power save state. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2129,6 +2236,31 @@ enum nl80211_attrs { NL80211_ATTR_REG_INDOOR, + NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS, + NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL, + NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS, + NL80211_ATTR_SCHED_SCAN_PLANS, + + NL80211_ATTR_PBSS, + + NL80211_ATTR_BSS_SELECT, + + NL80211_ATTR_STA_SUPPORT_P2P_PS, + + NL80211_ATTR_PAD, + + NL80211_ATTR_IFTYPE_EXT_CAPA, + + NL80211_ATTR_MU_MIMO_GROUP_DATA, + NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR, + + NL80211_ATTR_SCAN_START_TIME_TSF, + NL80211_ATTR_SCAN_START_TIME_TSF_BSSID, + NL80211_ATTR_MEASUREMENT_DURATION, + NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY, + + NL80211_ATTR_MESH_PEER_AID, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2272,6 +2404,20 @@ enum nl80211_sta_flags { NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 }; +/** + * enum nl80211_sta_p2p_ps_status - station support of P2P PS + * + * @NL80211_P2P_PS_UNSUPPORTED: station doesn't support P2P PS mechanism + * @@NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism + * @NUM_NL80211_P2P_PS_STATUS: number of values + */ +enum nl80211_sta_p2p_ps_status { + NL80211_P2P_PS_UNSUPPORTED = 0, + NL80211_P2P_PS_SUPPORTED, + + NUM_NL80211_P2P_PS_STATUS, +}; + #define NL80211_STA_FLAG_MAX_OLD_API NL80211_STA_FLAG_TDLS_PEER /** @@ -2429,6 +2575,9 @@ enum nl80211_sta_bss_param { * TID+1 and the special TID 16 (i.e. value 17) is used for non-QoS frames; * each one of those is again nested with &enum nl80211_tid_stats * attributes carrying the actual values. + * @NL80211_STA_INFO_RX_DURATION: aggregate PPDU duration for all frames + * received from the station (u64, usec) + * @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -2465,6 +2614,8 @@ enum nl80211_sta_info { NL80211_STA_INFO_BEACON_RX, NL80211_STA_INFO_BEACON_SIGNAL_AVG, NL80211_STA_INFO_TID_STATS, + NL80211_STA_INFO_RX_DURATION, + NL80211_STA_INFO_PAD, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, @@ -2481,6 +2632,7 @@ enum nl80211_sta_info { * transmitted MSDUs (not counting the first attempt; u64) * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted * MSDUs (u64) + * @NL80211_TID_STATS_PAD: attribute used for padding for 64-bit alignment * @NUM_NL80211_TID_STATS: number of attributes here * @NL80211_TID_STATS_MAX: highest numbered attribute here */ @@ -2490,6 +2642,7 @@ enum nl80211_tid_stats { NL80211_TID_STATS_TX_MSDU, NL80211_TID_STATS_TX_MSDU_RETRIES, NL80211_TID_STATS_TX_MSDU_FAILED, + NL80211_TID_STATS_PAD, /* keep last */ NUM_NL80211_TID_STATS, @@ -2619,16 +2772,17 @@ enum nl80211_band_attr { * an indoor surroundings, i.e., it is connected to AC power (and not * through portable DC inverters) or is under the control of a master * that is acting as an AP and is connected to AC power. - * @NL80211_FREQUENCY_ATTR_GO_CONCURRENT: GO operation is allowed on this + * @NL80211_FREQUENCY_ATTR_IR_CONCURRENT: IR operation is allowed on this * channel if it's connected concurrently to a BSS on the same channel on * the 2 GHz band or to a channel in the same UNII band (on the 5 GHz - * band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO on a - * channel that has the GO_CONCURRENT attribute set can be done when there - * is a clear assessment that the device is operating under the guidance of - * an authorized master, i.e., setting up a GO while the device is also - * connected to an AP with DFS and radar detection on the UNII band (it is - * up to user-space, i.e., wpa_supplicant to perform the required - * verifications) + * band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO or TDLS + * off-channel on a channel that has the IR_CONCURRENT attribute set can be + * done when there is a clear assessment that the device is operating under + * the guidance of an authorized master, i.e., setting up a GO or TDLS + * off-channel while the device is also connected to an AP with DFS and + * radar detection on the UNII band (it is up to user-space, i.e., + * wpa_supplicant to perform the required verifications). Using this + * attribute for IR is disallowed for master interfaces (IBSS, AP). * @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed @@ -2640,7 +2794,7 @@ enum nl80211_band_attr { * See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122 * for more information on the FCC description of the relaxations allowed * by NL80211_FREQUENCY_ATTR_INDOOR_ONLY and - * NL80211_FREQUENCY_ATTR_GO_CONCURRENT. + * NL80211_FREQUENCY_ATTR_IR_CONCURRENT. */ enum nl80211_frequency_attr { __NL80211_FREQUENCY_ATTR_INVALID, @@ -2658,7 +2812,7 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_NO_160MHZ, NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, NL80211_FREQUENCY_ATTR_INDOOR_ONLY, - NL80211_FREQUENCY_ATTR_GO_CONCURRENT, + NL80211_FREQUENCY_ATTR_IR_CONCURRENT, NL80211_FREQUENCY_ATTR_NO_20MHZ, NL80211_FREQUENCY_ATTR_NO_10MHZ, @@ -2671,6 +2825,8 @@ enum nl80211_frequency_attr { #define NL80211_FREQUENCY_ATTR_PASSIVE_SCAN NL80211_FREQUENCY_ATTR_NO_IR #define NL80211_FREQUENCY_ATTR_NO_IBSS NL80211_FREQUENCY_ATTR_NO_IR #define NL80211_FREQUENCY_ATTR_NO_IR NL80211_FREQUENCY_ATTR_NO_IR +#define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \ + NL80211_FREQUENCY_ATTR_IR_CONCURRENT /** * enum nl80211_bitrate_attr - bitrate attributes @@ -2829,7 +2985,7 @@ enum nl80211_sched_scan_match_attr { * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated * base on contiguous rules and wider channels will be allowed to cross * multiple contiguous/overlapping frequency ranges. - * @NL80211_RRF_GO_CONCURRENT: See &NL80211_FREQUENCY_ATTR_GO_CONCURRENT + * @NL80211_RRF_IR_CONCURRENT: See &NL80211_FREQUENCY_ATTR_IR_CONCURRENT * @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation * @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed @@ -2846,7 +3002,7 @@ enum nl80211_reg_rule_flags { NL80211_RRF_NO_IR = 1<<7, __NL80211_RRF_NO_IBSS = 1<<8, NL80211_RRF_AUTO_BW = 1<<11, - NL80211_RRF_GO_CONCURRENT = 1<<12, + NL80211_RRF_IR_CONCURRENT = 1<<12, NL80211_RRF_NO_HT40MINUS = 1<<13, NL80211_RRF_NO_HT40PLUS = 1<<14, NL80211_RRF_NO_80MHZ = 1<<15, @@ -2858,6 +3014,7 @@ enum nl80211_reg_rule_flags { #define NL80211_RRF_NO_IR NL80211_RRF_NO_IR #define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS |\ NL80211_RRF_NO_HT40PLUS) +#define NL80211_RRF_GO_CONCURRENT NL80211_RRF_IR_CONCURRENT /* For backport compatibility with older userspace */ #define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS) @@ -2922,6 +3079,7 @@ enum nl80211_user_reg_hint_type { * transmitting data (on channel or globally) * @NL80211_SURVEY_INFO_TIME_SCAN: time the radio spent for scan * (on this channel or globally) + * @NL80211_SURVEY_INFO_PAD: attribute used for padding for 64-bit alignment * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number * currently defined * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use @@ -2937,6 +3095,7 @@ enum nl80211_survey_info { NL80211_SURVEY_INFO_TIME_RX, NL80211_SURVEY_INFO_TIME_TX, NL80211_SURVEY_INFO_TIME_SCAN, + NL80211_SURVEY_INFO_PAD, /* keep last */ __NL80211_SURVEY_INFO_AFTER_LAST, @@ -3359,6 +3518,16 @@ enum nl80211_bss_scan_width { * (not present if no beacon frame has been received yet) * @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and * @NL80211_BSS_TSF is known to be from a probe response (flag attribute) + * @NL80211_BSS_LAST_SEEN_BOOTTIME: CLOCK_BOOTTIME timestamp when this entry + * was last updated by a received frame. The value is expected to be + * accurate to about 10ms. (u64, nanoseconds) + * @NL80211_BSS_PAD: attribute used for padding for 64-bit alignment + * @NL80211_BSS_PARENT_TSF: the time at the start of reception of the first + * octet of the timestamp field of the last beacon/probe received for + * this BSS. The time is the TSF of the BSS specified by + * @NL80211_BSS_PARENT_BSSID. (u64). + * @NL80211_BSS_PARENT_BSSID: the BSS according to which @NL80211_BSS_PARENT_TSF + * is set. * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ @@ -3378,6 +3547,10 @@ enum nl80211_bss { NL80211_BSS_CHAN_WIDTH, NL80211_BSS_BEACON_TSF, NL80211_BSS_PRESP_DATA, + NL80211_BSS_LAST_SEEN_BOOTTIME, + NL80211_BSS_PAD, + NL80211_BSS_PARENT_TSF, + NL80211_BSS_PARENT_BSSID, /* keep last */ __NL80211_BSS_AFTER_LAST, @@ -3563,11 +3736,15 @@ enum nl80211_txrate_gi { * @NL80211_BAND_2GHZ: 2.4 GHz ISM band * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz) + * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace + * since newer kernel versions may support more bands */ enum nl80211_band { NL80211_BAND_2GHZ, NL80211_BAND_5GHZ, NL80211_BAND_60GHZ, + + NUM_NL80211_BANDS, }; /** @@ -4353,12 +4530,38 @@ enum nl80211_feature_flags { /** * enum nl80211_ext_feature_index - bit index of extended features. * @NL80211_EXT_FEATURE_VHT_IBSS: This driver supports IBSS with VHT datarates. + * @NL80211_EXT_FEATURE_RRM: This driver supports RRM. When featured, user can + * can request to use RRM (see %NL80211_ATTR_USE_RRM) with + * %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests, which will set + * the ASSOC_REQ_USE_RRM flag in the association request even if + * NL80211_FEATURE_QUIET is not advertized. + * @NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER: This device supports MU-MIMO air + * sniffer which means that it can be configured to hear packets from + * certain groups which can be configured by the + * %NL80211_ATTR_MU_MIMO_GROUP_DATA attribute, + * or can be configured to follow a station by configuring the + * %NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR attribute. + * @NL80211_EXT_FEATURE_SCAN_START_TIME: This driver includes the actual + * time the scan started in scan results event. The time is the TSF of + * the BSS that the interface that requested the scan is connected to + * (if available). + * @NL80211_EXT_FEATURE_BSS_PARENT_TSF: Per BSS, this driver reports the + * time the last beacon/probe was received. The time is the TSF of the + * BSS that the interface that requested the scan is connected to + * (if available). + * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of + * channel dwell time. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_VHT_IBSS, + NL80211_EXT_FEATURE_RRM, + NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER, + NL80211_EXT_FEATURE_SCAN_START_TIME, + NL80211_EXT_FEATURE_BSS_PARENT_TSF, + NL80211_EXT_FEATURE_SET_SCAN_DWELL, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, @@ -4584,4 +4787,72 @@ enum nl80211_tdls_peer_capability { NL80211_TDLS_PEER_WMM = 1<<2, }; +/** + * enum nl80211_sched_scan_plan - scanning plan for scheduled scan + * @__NL80211_SCHED_SCAN_PLAN_INVALID: attribute number 0 is reserved + * @NL80211_SCHED_SCAN_PLAN_INTERVAL: interval between scan iterations. In + * seconds (u32). + * @NL80211_SCHED_SCAN_PLAN_ITERATIONS: number of scan iterations in this + * scan plan (u32). The last scan plan must not specify this attribute + * because it will run infinitely. A value of zero is invalid as it will + * make the scan plan meaningless. + * @NL80211_SCHED_SCAN_PLAN_MAX: highest scheduled scan plan attribute number + * currently defined + * @__NL80211_SCHED_SCAN_PLAN_AFTER_LAST: internal use + */ +enum nl80211_sched_scan_plan { + __NL80211_SCHED_SCAN_PLAN_INVALID, + NL80211_SCHED_SCAN_PLAN_INTERVAL, + NL80211_SCHED_SCAN_PLAN_ITERATIONS, + + /* keep last */ + __NL80211_SCHED_SCAN_PLAN_AFTER_LAST, + NL80211_SCHED_SCAN_PLAN_MAX = + __NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1 +}; + +/** + * struct nl80211_bss_select_rssi_adjust - RSSI adjustment parameters. + * + * @band: band of BSS that must match for RSSI value adjustment. + * @delta: value used to adjust the RSSI value of matching BSS. + */ +struct nl80211_bss_select_rssi_adjust { + __u8 band; + __s8 delta; +} __attribute__((packed)); + +/** + * enum nl80211_bss_select_attr - attributes for bss selection. + * + * @__NL80211_BSS_SELECT_ATTR_INVALID: reserved. + * @NL80211_BSS_SELECT_ATTR_RSSI: Flag indicating only RSSI-based BSS selection + * is requested. + * @NL80211_BSS_SELECT_ATTR_BAND_PREF: attribute indicating BSS + * selection should be done such that the specified band is preferred. + * When there are multiple BSS-es in the preferred band, the driver + * shall use RSSI-based BSS selection as a second step. The value of + * this attribute is according to &enum nl80211_band (u32). + * @NL80211_BSS_SELECT_ATTR_RSSI_ADJUST: When present the RSSI level for + * BSS-es in the specified band is to be adjusted before doing + * RSSI-based BSS selection. The attribute value is a packed structure + * value as specified by &struct nl80211_bss_select_rssi_adjust. + * @NL80211_BSS_SELECT_ATTR_MAX: highest bss select attribute number. + * @__NL80211_BSS_SELECT_ATTR_AFTER_LAST: internal use. + * + * One and only one of these attributes are found within %NL80211_ATTR_BSS_SELECT + * for %NL80211_CMD_CONNECT. It specifies the required BSS selection behaviour + * which the driver shall use. + */ +enum nl80211_bss_select_attr { + __NL80211_BSS_SELECT_ATTR_INVALID, + NL80211_BSS_SELECT_ATTR_RSSI, + NL80211_BSS_SELECT_ATTR_BAND_PREF, + NL80211_BSS_SELECT_ATTR_RSSI_ADJUST, + + /* keep last */ + __NL80211_BSS_SELECT_ATTR_AFTER_LAST, + NL80211_BSS_SELECT_ATTR_MAX = __NL80211_BSS_SELECT_ATTR_AFTER_LAST - 1 +}; + #endif /* __LINUX_NL80211_H */ diff --git a/src/drivers/rfkill.c b/src/drivers/rfkill.c index 45b26c46b69c7..4d4d1b4489fd4 100644 --- a/src/drivers/rfkill.c +++ b/src/drivers/rfkill.c @@ -8,6 +8,7 @@ #include "includes.h" #include <fcntl.h> +#include <limits.h> #include "utils/common.h" #include "utils/eloop.h" @@ -47,6 +48,7 @@ struct rfkill_data { struct rfkill_config *cfg; int fd; int blocked; + uint32_t idx; }; @@ -69,12 +71,13 @@ static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx) (int) len, RFKILL_EVENT_SIZE_V1); return; } + if (event.op != RFKILL_OP_CHANGE || event.idx != rfkill->idx) + return; + wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d " "op=%u soft=%u hard=%u", event.idx, event.type, event.op, event.soft, event.hard); - if (event.op != RFKILL_OP_CHANGE || event.type != RFKILL_TYPE_WLAN) - return; if (event.hard) { wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked"); @@ -102,11 +105,23 @@ struct rfkill_data * rfkill_init(struct rfkill_config *cfg) struct rfkill_data *rfkill; struct rfkill_event event; ssize_t len; + char *phy = NULL, *rfk_phy; + char buf[24 + IFNAMSIZ + 1]; + char buf2[31 + 11 + 1]; + int found = 0; rfkill = os_zalloc(sizeof(*rfkill)); if (rfkill == NULL) return NULL; + os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/phy80211", + cfg->ifname); + phy = realpath(buf, NULL); + if (!phy) { + wpa_printf(MSG_INFO, "rfkill: Cannot get wiphy information"); + goto fail; + } + rfkill->cfg = cfg; rfkill->fd = open("/dev/rfkill", O_RDONLY); if (rfkill->fd < 0) { @@ -136,13 +151,27 @@ struct rfkill_data * rfkill_init(struct rfkill_config *cfg) (int) len, RFKILL_EVENT_SIZE_V1); continue; } + if (event.op != RFKILL_OP_ADD || + event.type != RFKILL_TYPE_WLAN) + continue; + + os_snprintf(buf2, sizeof(buf2), + "/sys/class/rfkill/rfkill%d/device", event.idx); + rfk_phy = realpath(buf2, NULL); + if (!rfk_phy) + goto fail2; + found = os_strcmp(phy, rfk_phy) == 0; + free(rfk_phy); + + if (!found) + continue; + wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d " "op=%u soft=%u hard=%u", event.idx, event.type, event.op, event.soft, event.hard); - if (event.op != RFKILL_OP_ADD || - event.type != RFKILL_TYPE_WLAN) - continue; + + rfkill->idx = event.idx; if (event.hard) { wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked"); rfkill->blocked = 1; @@ -150,8 +179,13 @@ struct rfkill_data * rfkill_init(struct rfkill_config *cfg) wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked"); rfkill->blocked = 1; } + break; } + if (!found) + goto fail2; + + free(phy); eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL); return rfkill; @@ -160,6 +194,8 @@ fail2: close(rfkill->fd); fail: os_free(rfkill); + /* use standard free function to match realpath() */ + free(phy); return NULL; } diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c index 4dfdb3f9c96bd..621746821538b 100644 --- a/src/eap_common/eap_eke_common.c +++ b/src/eap_common/eap_eke_common.c @@ -44,9 +44,7 @@ static int eap_eke_dhcomp_len(u8 dhgroup, u8 encr) int dhlen; dhlen = eap_eke_dh_len(dhgroup); - if (dhlen < 0) - return -1; - if (encr != EAP_EKE_ENCR_AES128_CBC) + if (dhlen < 0 || encr != EAP_EKE_ENCR_AES128_CBC) return -1; return AES_BLOCK_SIZE + dhlen; } @@ -166,13 +164,10 @@ int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub) size_t pub_len, i; generator = eap_eke_dh_generator(group); - if (generator < 0 || generator > 255) - return -1; - gen = generator; - dh = eap_eke_dh_group(group); - if (dh == NULL) + if (generator < 0 || generator > 255 || !dh) return -1; + gen = generator; /* x = random number 2 .. p-1 */ if (random_get_bytes(ret_priv, dh->prime_len)) @@ -411,11 +406,8 @@ int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, size_t len; const struct dh_group *dh; - if (sess->encr != EAP_EKE_ENCR_AES128_CBC) - return -1; - dh = eap_eke_dh_group(sess->dhgroup); - if (dh == NULL) + if (sess->encr != EAP_EKE_ENCR_AES128_CBC || !dh) return -1; /* Decrypt peer DHComponent */ @@ -635,6 +627,7 @@ int eap_eke_prot(struct eap_eke_session *sess, if (*prot_len < block_size + data_len + pad + icv_len) { wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for Prot() data"); + return -1; } pos = prot; @@ -653,10 +646,8 @@ int eap_eke_prot(struct eap_eke_session *sess, pos += pad; } - if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0) - return -1; - - if (eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0) + if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0 || + eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0) return -1; pos += icv_len; @@ -684,9 +675,8 @@ int eap_eke_decrypt_prot(struct eap_eke_session *sess, else return -1; - if (prot_len < 2 * block_size + icv_len) - return -1; - if ((prot_len - icv_len) % block_size) + if (prot_len < 2 * block_size + icv_len || + (prot_len - icv_len) % block_size) return -1; if (eap_eke_mac(sess->mac, sess->ki, prot + block_size, @@ -737,22 +727,14 @@ int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr, sess->mac = mac; sess->prf_len = eap_eke_prf_len(prf); - if (sess->prf_len < 0) - return -1; sess->nonce_len = eap_eke_nonce_len(prf); - if (sess->nonce_len < 0) - return -1; sess->auth_len = eap_eke_auth_len(prf); - if (sess->auth_len < 0) - return -1; sess->dhcomp_len = eap_eke_dhcomp_len(sess->dhgroup, sess->encr); - if (sess->dhcomp_len < 0) - return -1; sess->pnonce_len = eap_eke_pnonce_len(sess->mac); - if (sess->pnonce_len < 0) - return -1; sess->pnonce_ps_len = eap_eke_pnonce_ps_len(sess->mac); - if (sess->pnonce_ps_len < 0) + if (sess->prf_len < 0 || sess->nonce_len < 0 || sess->auth_len < 0 || + sess->dhcomp_len < 0 || sess->pnonce_len < 0 || + sess->pnonce_ps_len < 0) return -1; return 0; diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c index 151cc7859c5da..9ef671c41c7d7 100644 --- a/src/eap_common/eap_fast_common.c +++ b/src/eap_common/eap_fast_common.c @@ -93,8 +93,7 @@ void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random, } -u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, - const char *label, size_t len) +u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, size_t len) { u8 *out; @@ -102,7 +101,7 @@ u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, if (out == NULL) return NULL; - if (tls_connection_prf(ssl_ctx, conn, label, 1, 1, out, len)) { + if (tls_connection_get_eap_fast_key(ssl_ctx, conn, out, len)) { os_free(out); return NULL; } @@ -111,22 +110,24 @@ u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, } -void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk) +int eap_fast_derive_eap_msk(const u8 *simck, u8 *msk) { /* * RFC 4851, Section 5.4: EAP Master Session Key Generation * MSK = T-PRF(S-IMCK[j], "Session Key Generating Function", 64) */ - sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, - "Session Key Generating Function", (u8 *) "", 0, - msk, EAP_FAST_KEY_LEN); + if (sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, + "Session Key Generating Function", (u8 *) "", 0, + msk, EAP_FAST_KEY_LEN) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)", msk, EAP_FAST_KEY_LEN); + return 0; } -void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk) +int eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk) { /* * RFC 4851, Section 5.4: EAP Master Session Key Genreration @@ -134,11 +135,13 @@ void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk) * "Extended Session Key Generating Function", 64) */ - sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, - "Extended Session Key Generating Function", (u8 *) "", 0, - emsk, EAP_EMSK_LEN); + if (sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, + "Extended Session Key Generating Function", (u8 *) "", 0, + emsk, EAP_EMSK_LEN) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)", emsk, EAP_EMSK_LEN); + return 0; } diff --git a/src/eap_common/eap_fast_common.h b/src/eap_common/eap_fast_common.h index d59a8450ba8c7..724204cb5e325 100644 --- a/src/eap_common/eap_fast_common.h +++ b/src/eap_common/eap_fast_common.h @@ -98,9 +98,9 @@ struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf); void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random, const u8 *client_random, u8 *master_secret); u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, - const char *label, size_t len); -void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk); -void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk); + size_t len); +int eap_fast_derive_eap_msk(const u8 *simck, u8 *msk); +int eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk); int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv, int tlv_type, u8 *pos, size_t len); diff --git a/src/eap_common/eap_gpsk_common.c b/src/eap_common/eap_gpsk_common.c index 8c7ae27b933c2..b0818797025f2 100644 --- a/src/eap_common/eap_gpsk_common.c +++ b/src/eap_common/eap_gpsk_common.c @@ -92,7 +92,8 @@ static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */, n = (len + hashlen - 1) / hashlen; for (i = 1; i <= n; i++) { WPA_PUT_BE16(ibuf, i); - hmac_sha256_vector(psk, 32, 2, addr, vlen, hash); + if (hmac_sha256_vector(psk, 32, 2, addr, vlen, hash)) + return -1; clen = left > hashlen ? hashlen : left; os_memcpy(opos, hash, clen); opos += clen; @@ -534,8 +535,7 @@ int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, break; #ifdef EAP_GPSK_SHA256 case EAP_GPSK_CIPHER_SHA256: - hmac_sha256(sk, sk_len, data, len, mic); - ret = 0; + ret = hmac_sha256(sk, sk_len, data, len, mic); break; #endif /* EAP_GPSK_SHA256 */ default: @@ -545,5 +545,8 @@ int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, break; } + if (ret) + wpa_printf(MSG_DEBUG, "EAP-GPSK: Could not compute MIC"); + return ret; } diff --git a/src/eap_common/eap_pax_common.c b/src/eap_common/eap_pax_common.c index 0e80ef511c113..a11bce8f9ba32 100644 --- a/src/eap_common/eap_pax_common.c +++ b/src/eap_common/eap_pax_common.c @@ -57,7 +57,8 @@ int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, left = output_len; for (counter = 1; counter <= (u8) num_blocks; counter++) { size_t clen = left > EAP_PAX_MAC_LEN ? EAP_PAX_MAC_LEN : left; - hmac_sha1_vector(key, key_len, 3, addr, len, mac); + if (hmac_sha1_vector(key, key_len, 3, addr, len, mac) < 0) + return -1; os_memcpy(pos, mac, clen); pos += clen; left -= clen; @@ -106,7 +107,8 @@ int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, len[2] = data3_len; count = (data1 ? 1 : 0) + (data2 ? 1 : 0) + (data3 ? 1 : 0); - hmac_sha1_vector(key, key_len, count, addr, len, hash); + if (hmac_sha1_vector(key, key_len, count, addr, len, hash) < 0) + return -1; os_memcpy(mac, hash, EAP_PAX_MAC_LEN); return 0; diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c index 4d27623f87bfd..67f8f7098c4b6 100644 --- a/src/eap_common/eap_pwd_common.c +++ b/src/eap_common/eap_pwd_common.c @@ -115,6 +115,26 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, case 26: nid = NID_secp224r1; break; +#ifdef NID_brainpoolP224r1 + case 27: + nid = NID_brainpoolP224r1; + break; +#endif /* NID_brainpoolP224r1 */ +#ifdef NID_brainpoolP256r1 + case 28: + nid = NID_brainpoolP256r1; + break; +#endif /* NID_brainpoolP256r1 */ +#ifdef NID_brainpoolP384r1 + case 29: + nid = NID_brainpoolP384r1; + break; +#endif /* NID_brainpoolP384r1 */ +#ifdef NID_brainpoolP512r1 + case 30: + nid = NID_brainpoolP512r1; + break; +#endif /* NID_brainpoolP512r1 */ default: wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num); return -1; diff --git a/src/eap_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c index c22e43ed84b60..8819541b2264c 100644 --- a/src/eap_common/eap_sake_common.c +++ b/src/eap_common/eap_sake_common.c @@ -121,7 +121,7 @@ static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr, attr->next_tmpid_len = len; break; case EAP_SAKE_AT_MSK_LIFE: - wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV"); + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MSK_LIFE"); if (len != 4) { wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid " "AT_MSK_LIFE payload length %d", len); diff --git a/src/eap_common/ikev2_common.c b/src/eap_common/ikev2_common.c index d60358c733f0b..90fb89e243b83 100644 --- a/src/eap_common/ikev2_common.c +++ b/src/eap_common/ikev2_common.c @@ -62,13 +62,15 @@ int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data, case AUTH_HMAC_SHA1_96: if (key_len != 20) return -1; - hmac_sha1(key, key_len, data, data_len, tmphash); + if (hmac_sha1(key, key_len, data, data_len, tmphash) < 0) + return -1; os_memcpy(hash, tmphash, 12); break; case AUTH_HMAC_MD5_96: if (key_len != 16) return -1; - hmac_md5(key, key_len, data, data_len, tmphash); + if (hmac_md5(key, key_len, data, data_len, tmphash) < 0) + return -1; os_memcpy(hash, tmphash, 12); break; default: @@ -98,16 +100,13 @@ int ikev2_prf_hash(int alg, const u8 *key, size_t key_len, { switch (alg) { case PRF_HMAC_SHA1: - hmac_sha1_vector(key, key_len, num_elem, addr, len, hash); - break; + return hmac_sha1_vector(key, key_len, num_elem, addr, len, + hash); case PRF_HMAC_MD5: - hmac_md5_vector(key, key_len, num_elem, addr, len, hash); - break; + return hmac_md5_vector(key, key_len, num_elem, addr, len, hash); default: return -1; } - - return 0; } diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index 56c24b5503200..9110ca5b9cfd6 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -48,6 +48,8 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req); static const char * eap_sm_method_state_txt(EapMethodState state); static const char * eap_sm_decision_txt(EapDecision decision); #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, + const char *msg, size_t msglen); @@ -188,6 +190,14 @@ SM_STATE(EAP, INITIALIZE) */ eapol_set_bool(sm, EAPOL_eapResp, FALSE); eapol_set_bool(sm, EAPOL_eapNoResp, FALSE); + /* + * RFC 4137 does not reset ignore here, but since it is possible for + * some method code paths to end up not setting ignore=FALSE, clear the + * value here to avoid issues if a previous authentication attempt + * failed with ignore=TRUE being left behind in the last + * m.check(eapReqData) operation. + */ + sm->ignore = 0; sm->num_rounds = 0; sm->prev_failure = 0; sm->expected_failure = 0; @@ -312,11 +322,14 @@ SM_STATE(EAP, GET_METHOD) wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: " "vendor %u method %u (%s)", sm->reqVendor, method, sm->m->name); - if (reinit) + if (reinit) { sm->eap_method_priv = sm->m->init_for_reauth( sm, sm->eap_method_priv); - else + } else { + sm->waiting_ext_cert_check = 0; + sm->ext_cert_check = 0; sm->eap_method_priv = sm->m->init(sm); + } if (sm->eap_method_priv == NULL) { struct eap_peer_config *config = eap_get_config(sm); @@ -1373,13 +1386,10 @@ static int eap_sm_imsi_identity(struct eap_sm *sm, return 0; } -#endif /* PCSC_FUNCS */ - static int eap_sm_set_scard_pin(struct eap_sm *sm, struct eap_peer_config *conf) { -#ifdef PCSC_FUNCS if (scard_set_pin(sm->scard_ctx, conf->pin)) { /* * Make sure the same PIN is not tried again in order to avoid @@ -1393,24 +1403,20 @@ static int eap_sm_set_scard_pin(struct eap_sm *sm, return -1; } return 0; -#else /* PCSC_FUNCS */ - return -1; -#endif /* PCSC_FUNCS */ } + static int eap_sm_get_scard_identity(struct eap_sm *sm, struct eap_peer_config *conf) { -#ifdef PCSC_FUNCS if (eap_sm_set_scard_pin(sm, conf)) return -1; return eap_sm_imsi_identity(sm, conf); -#else /* PCSC_FUNCS */ - return -1; -#endif /* PCSC_FUNCS */ } +#endif /* PCSC_FUNCS */ + /** * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network @@ -1453,23 +1459,27 @@ struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted) identity, identity_len); } - if (identity == NULL) { - wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity " - "configuration was not available"); - if (config->pcsc) { + if (config->pcsc) { +#ifdef PCSC_FUNCS + if (!identity) { if (eap_sm_get_scard_identity(sm, config) < 0) return NULL; identity = config->identity; identity_len = config->identity_len; - wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " - "IMSI", identity, identity_len); - } else { - eap_sm_request_identity(sm); + wpa_hexdump_ascii(MSG_DEBUG, + "permanent identity from IMSI", + identity, identity_len); + } else if (eap_sm_set_scard_pin(sm, config) < 0) { return NULL; } - } else if (config->pcsc) { - if (eap_sm_set_scard_pin(sm, config) < 0) - return NULL; +#else /* PCSC_FUNCS */ + return NULL; +#endif /* PCSC_FUNCS */ + } else if (!identity) { + wpa_printf(MSG_WARNING, + "EAP: buildIdentity: identity configuration was not available"); + eap_sm_request_identity(sm); + return NULL; } resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len, @@ -1510,15 +1520,9 @@ static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req) static struct wpabuf * eap_sm_buildNotify(int id) { - struct wpabuf *resp; - wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification"); - resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0, - EAP_CODE_RESPONSE, id); - if (resp == NULL) - return NULL; - - return resp; + return eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0, + EAP_CODE_RESPONSE, id); } @@ -1850,6 +1854,11 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, case TLS_CERT_CHAIN_SUCCESS: eap_notify_status(sm, "remote certificate verification", "success"); + if (sm->ext_cert_check) { + sm->waiting_ext_cert_check = 1; + eap_sm_request(sm, WPA_CTRL_REQ_EXT_CERT_CHECK, + NULL, 0); + } break; case TLS_CERT_CHAIN_FAILURE: wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR @@ -2172,10 +2181,10 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) #endif /* CONFIG_CTRL_IFACE */ -#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, const char *msg, size_t msglen) { +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) struct eap_peer_config *config; const char *txt = NULL; char *tmp; @@ -2224,16 +2233,17 @@ static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, case WPA_CTRL_REQ_SIM: txt = msg; break; + case WPA_CTRL_REQ_EXT_CERT_CHECK: + break; default: return; } if (sm->eapol_cb->eap_param_needed) sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt); -} -#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ -#define eap_sm_request(sm, type, msg, msglen) do { } while (0) #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +} + const char * eap_sm_get_method_name(struct eap_sm *sm) { diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index dc9e8cc34d4af..0bac62dee523a 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -1492,7 +1492,6 @@ static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_aka_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); @@ -1511,10 +1510,7 @@ int eap_peer_aka_register(void) eap->get_identity = eap_aka_get_identity; eap->get_emsk = eap_aka_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } @@ -1522,7 +1518,6 @@ int eap_peer_aka_register(void) int eap_peer_aka_prime_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, @@ -1542,10 +1537,6 @@ int eap_peer_aka_prime_register(void) eap->get_identity = eap_aka_get_identity; eap->get_emsk = eap_aka_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - - return ret; + return eap_peer_method_register(eap); } #endif /* EAP_AKA_PRIME */ diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index 2b1a1d5e4b254..f98007263b338 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -181,13 +181,13 @@ struct eap_peer_config { * subject_match - Constraint for server certificate subject * * This substring is matched against the subject of the authentication - * server certificate. If this string is set, the server sertificate is + * server certificate. If this string is set, the server certificate is * only accepted if it contains this string in the subject. The subject * string is in following format: * * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com * - * Note: Since this is a substring match, this cannot be used securily + * Note: Since this is a substring match, this cannot be used securely * 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. */ @@ -198,7 +198,7 @@ struct eap_peer_config { * * Semicolon separated string of entries to be matched against the * alternative subject name of the authentication server certificate. - * If this string is set, the server sertificate is only accepted if it + * If this string is set, the server certificate is only accepted if it * contains one of the entries in an alternative subject name * extension. * @@ -739,6 +739,20 @@ struct eap_peer_config { * erp - Whether EAP Re-authentication Protocol (ERP) is enabled */ int erp; + + /** + * pending_ext_cert_check - External server certificate check status + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request external + * validation of server certificate chain. + */ + enum { + NO_CHECK = 0, + PENDING_CHECK, + EXT_CERT_CHECK_GOOD, + EXT_CERT_CHECK_BAD, + } pending_ext_cert_check; }; diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c index dfbda5644f6fc..f899f653fdca4 100644 --- a/src/eap_peer/eap_eke.c +++ b/src/eap_peer/eap_eke.c @@ -452,6 +452,7 @@ static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, /* DHComponent_P = Encr(key, y_p) */ rpos = wpabuf_put(resp, data->sess.dhcomp_len); if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) { + wpabuf_free(resp); wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_P"); os_memset(key, 0, sizeof(key)); return eap_eke_build_fail(data, ret, id, @@ -770,7 +771,6 @@ static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_eke_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); @@ -785,8 +785,5 @@ int eap_peer_eke_register(void) eap->get_emsk = eap_eke_get_emsk; eap->getSessionId = eap_eke_get_session_id; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c index 4cbe3bacb0a61..964ebe74fede8 100644 --- a/src/eap_peer/eap_fast.c +++ b/src/eap_peer/eap_fast.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-FAST (RFC 4851) - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -67,6 +67,7 @@ struct eap_fast_data { int simck_idx; struct wpabuf *pending_phase2_req; + struct wpabuf *pending_resp; }; @@ -112,8 +113,8 @@ static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, } -static int eap_fast_parse_phase1(struct eap_fast_data *data, - const char *phase1) +static void eap_fast_parse_phase1(struct eap_fast_data *data, + const char *phase1) { const char *pos; @@ -139,8 +140,6 @@ static int eap_fast_parse_phase1(struct eap_fast_data *data, wpa_printf(MSG_DEBUG, "EAP-FAST: Using binary format for PAC " "list"); } - - return 0; } @@ -158,10 +157,8 @@ static void * eap_fast_init(struct eap_sm *sm) data->fast_version = EAP_FAST_VERSION; data->max_pac_list_len = 10; - if (config->phase1 && eap_fast_parse_phase1(data, config->phase1) < 0) { - eap_fast_deinit(sm, data); - return NULL; - } + if (config->phase1) + eap_fast_parse_phase1(data, config->phase1); if (eap_peer_select_phase2_methods(config, "auth=", &data->phase2_types, @@ -254,14 +251,16 @@ static void eap_fast_deinit(struct eap_sm *sm, void *priv) os_memset(data->emsk, 0, EAP_EMSK_LEN); os_free(data->session_id); wpabuf_free(data->pending_phase2_req); + wpabuf_free(data->pending_resp); os_free(data); } static int eap_fast_derive_msk(struct eap_fast_data *data) { - eap_fast_derive_eap_msk(data->simck, data->key_data); - eap_fast_derive_eap_emsk(data->simck, data->emsk); + if (eap_fast_derive_eap_msk(data->simck, data->key_data) < 0 || + eap_fast_derive_eap_emsk(data->simck, data->emsk) < 0) + return -1; data->success = 1; return 0; } @@ -276,7 +275,7 @@ static int eap_fast_derive_key_auth(struct eap_sm *sm, * Extra key material after TLS key_block: session_key_seed[40] */ - sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion", + sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, EAP_FAST_SKS_LEN); if (sks == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " @@ -304,7 +303,6 @@ static int eap_fast_derive_key_provisioning(struct eap_sm *sm, os_free(data->key_block_p); data->key_block_p = (struct eap_fast_key_block_provisioning *) eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, - "key expansion", sizeof(*data->key_block_p)); if (data->key_block_p == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block"); @@ -711,9 +709,10 @@ static int eap_fast_get_cmk(struct eap_sm *sm, struct eap_fast_data *data, if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0) return -1; wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk)); - sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN, - "Inner Methods Compound Keys", - isk, sizeof(isk), imck, sizeof(imck)); + if (sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)) < 0) + return -1; data->simck_idx++; os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]", @@ -1096,7 +1095,7 @@ static int eap_fast_parse_decrypted(struct wpabuf *decrypted, /* Parse TLVs from the decrypted Phase 2 data */ pos = wpabuf_mhead(decrypted); end = pos + wpabuf_len(decrypted); - while (pos + 4 < end) { + while (end - pos > 4) { mandatory = pos[0] & 0x80; tlv_type = WPA_GET_BE16(pos) & 0x3fff; pos += 2; @@ -1443,7 +1442,7 @@ static int eap_fast_clear_pac_opaque_ext(struct eap_sm *sm, static int eap_fast_set_provisioning_ciphers(struct eap_sm *sm, struct eap_fast_data *data) { - u8 ciphers[5]; + u8 ciphers[7]; int count = 0; if (data->provisioning_allowed & EAP_FAST_PROV_UNAUTH) { @@ -1455,7 +1454,9 @@ static int eap_fast_set_provisioning_ciphers(struct eap_sm *sm, if (data->provisioning_allowed & EAP_FAST_PROV_AUTH) { wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling authenticated " "provisioning TLS cipher suites"); + ciphers[count++] = TLS_CIPHER_RSA_DHE_AES256_SHA; ciphers[count++] = TLS_CIPHER_RSA_DHE_AES128_SHA; + ciphers[count++] = TLS_CIPHER_AES256_SHA; ciphers[count++] = TLS_CIPHER_AES128_SHA; ciphers[count++] = TLS_CIPHER_RC4_SHA; } @@ -1567,6 +1568,34 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, res = 1; } } else { + if (sm->waiting_ext_cert_check && data->pending_resp) { + struct eap_peer_config *config = eap_get_config(sm); + + if (config->pending_ext_cert_check == + EXT_CERT_CHECK_GOOD) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: External certificate check succeeded - continue handshake"); + resp = data->pending_resp; + data->pending_resp = NULL; + sm->waiting_ext_cert_check = 0; + return resp; + } + + if (config->pending_ext_cert_check == + EXT_CERT_CHECK_BAD) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: External certificate check failed - force authentication failure"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + sm->waiting_ext_cert_check = 0; + return NULL; + } + + wpa_printf(MSG_DEBUG, + "EAP-FAST: Continuing to wait external server certificate validation"); + return NULL; + } + /* Continue processing TLS handshake (phase 1). */ res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_FAST, @@ -1580,6 +1609,14 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, return resp; } + if (sm->waiting_ext_cert_check) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: Waiting external server certificate validation"); + wpabuf_free(data->pending_resp); + data->pending_resp = resp; + return NULL; + } + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { char cipher[80]; wpa_printf(MSG_DEBUG, @@ -1644,6 +1681,8 @@ static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv) data->key_block_p = NULL; wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = NULL; + wpabuf_free(data->pending_resp); + data->pending_resp = NULL; } @@ -1721,7 +1760,7 @@ static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len) struct eap_fast_data *data = priv; u8 *id; - if (!data->success) + if (!data->success || !data->session_id) return NULL; id = os_malloc(data->id_len); @@ -1757,7 +1796,6 @@ static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_fast_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST"); @@ -1778,8 +1816,5 @@ int eap_peer_fast_register(void) #endif eap->get_emsk = eap_fast_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c index 89e604ecf84b0..c815860355137 100644 --- a/src/eap_peer/eap_fast_pac.c +++ b/src/eap_peer/eap_fast_pac.c @@ -455,7 +455,8 @@ int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root, } if (pac) { - err = "PAC block not terminated with END"; + if (!err) + err = "PAC block not terminated with END"; eap_fast_free_pac(pac); } @@ -709,7 +710,7 @@ static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac) pos = pac->pac_info; end = pos + pac->pac_info_len; - while (pos + 4 < end) { + while (end - pos > 4) { type = WPA_GET_BE16(pos); pos += 2; len = WPA_GET_BE16(pos); @@ -801,8 +802,10 @@ int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, while (pos < end) { u16 val; - if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2) + if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2) { + pac = NULL; goto parse_fail; + } pac = os_zalloc(sizeof(*pac)); if (pac == NULL) diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c index 902b4ba26d6e4..177cbccf58508 100644 --- a/src/eap_peer/eap_gpsk.c +++ b/src/eap_peer/eap_gpsk.c @@ -771,7 +771,6 @@ static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_gpsk_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK"); @@ -786,8 +785,5 @@ int eap_peer_gpsk_register(void) eap->get_emsk = eap_gpsk_get_emsk; eap->getSessionId = eap_gpsk_get_session_id; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_gtc.c b/src/eap_peer/eap_gtc.c index 9f3cfbdacadc8..a519a780a90b6 100644 --- a/src/eap_peer/eap_gtc.c +++ b/src/eap_peer/eap_gtc.c @@ -127,7 +127,6 @@ static struct wpabuf * eap_gtc_process(struct eap_sm *sm, void *priv, int eap_peer_gtc_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC"); @@ -138,8 +137,5 @@ int eap_peer_gtc_register(void) eap->deinit = eap_gtc_deinit; eap->process = eap_gtc_process; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index 99b44dae4e34e..6ab24834d654a 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -366,6 +366,8 @@ struct eap_sm { int external_sim; unsigned int expected_failure:1; + unsigned int ext_cert_check:1; + unsigned int waiting_ext_cert_check:1; struct dl_list erp_keys; /* struct eap_erp_key */ }; diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c index b5ef71bac3ba4..390f0ec8cf4df 100644 --- a/src/eap_peer/eap_ikev2.c +++ b/src/eap_peer/eap_ikev2.c @@ -513,7 +513,6 @@ static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_ikev2_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_IKEV2, @@ -529,8 +528,5 @@ int eap_peer_ikev2_register(void) eap->get_emsk = eap_ikev2_get_emsk; eap->getSessionId = eap_ikev2_get_session_id; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c index e0f8bcf6b0df7..ff6fa4afd2f72 100644 --- a/src/eap_peer/eap_leap.c +++ b/src/eap_peer/eap_leap.c @@ -393,7 +393,6 @@ static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_leap_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP"); @@ -406,8 +405,5 @@ int eap_peer_leap_register(void) eap->isKeyAvailable = eap_leap_isKeyAvailable; eap->getKey = eap_leap_getKey; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_md5.c b/src/eap_peer/eap_md5.c index d06befaeb1df5..efae8deba85d5 100644 --- a/src/eap_peer/eap_md5.c +++ b/src/eap_peer/eap_md5.c @@ -102,7 +102,6 @@ static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv, int eap_peer_md5_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5"); @@ -113,8 +112,5 @@ int eap_peer_md5_register(void) eap->deinit = eap_md5_deinit; eap->process = eap_md5_process; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_methods.c b/src/eap_peer/eap_methods.c index 1bdd81e1ad52a..9747954952a7b 100644 --- a/src/eap_peer/eap_methods.c +++ b/src/eap_peer/eap_methods.c @@ -18,6 +18,8 @@ static struct eap_method *eap_methods = NULL; +static void eap_peer_method_free(struct eap_method *method); + /** * eap_peer_get_eap_method - Get EAP method based on type number @@ -295,7 +297,7 @@ struct eap_method * eap_peer_method_alloc(int version, int vendor, * eap_peer_method_free - Free EAP peer method structure * @method: Method structure allocated with eap_peer_method_alloc() */ -void eap_peer_method_free(struct eap_method *method) +static void eap_peer_method_free(struct eap_method *method) { os_free(method); } @@ -303,26 +305,31 @@ void eap_peer_method_free(struct eap_method *method) /** * eap_peer_method_register - Register an EAP peer method - * @method: EAP method to register + * @method: EAP method to register from eap_peer_method_alloc() * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method * has already been registered * * Each EAP peer method needs to call this function to register itself as a - * supported EAP method. + * supported EAP method. The caller must not free the allocated method data + * regardless of the return value. */ int eap_peer_method_register(struct eap_method *method) { struct eap_method *m, *last = NULL; if (method == NULL || method->name == NULL || - method->version != EAP_PEER_METHOD_INTERFACE_VERSION) + method->version != EAP_PEER_METHOD_INTERFACE_VERSION) { + eap_peer_method_free(method); return -1; + } for (m = eap_methods; m; m = m->next) { if ((m->vendor == method->vendor && m->method == method->method) || - os_strcmp(m->name, method->name) == 0) + os_strcmp(m->name, method->name) == 0) { + eap_peer_method_free(method); return -2; + } last = m; } diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h index e35c919abce94..b96b211de2589 100644 --- a/src/eap_peer/eap_methods.h +++ b/src/eap_peer/eap_methods.h @@ -16,7 +16,6 @@ const struct eap_method * eap_peer_get_methods(size_t *count); struct eap_method * eap_peer_method_alloc(int version, int vendor, EapType method, const char *name); -void eap_peer_method_free(struct eap_method *method); int eap_peer_method_register(struct eap_method *method); diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c index 6acf1e8ad390a..ce2227d388ef4 100644 --- a/src/eap_peer/eap_mschapv2.c +++ b/src/eap_peer/eap_mschapv2.c @@ -880,7 +880,6 @@ static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_mschapv2_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, @@ -894,8 +893,5 @@ int eap_peer_mschapv2_register(void) eap->isKeyAvailable = eap_mschapv2_isKeyAvailable; eap->getKey = eap_mschapv2_getKey; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_otp.c b/src/eap_peer/eap_otp.c index 9ac744a7ddade..0ab4c7907ab5a 100644 --- a/src/eap_peer/eap_otp.c +++ b/src/eap_peer/eap_otp.c @@ -83,7 +83,6 @@ static struct wpabuf * eap_otp_process(struct eap_sm *sm, void *priv, int eap_peer_otp_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_OTP, "OTP"); @@ -94,8 +93,5 @@ int eap_peer_otp_register(void) eap->deinit = eap_otp_deinit; eap->process = eap_otp_process; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c index c920bcd3182f8..a7012d2870cf3 100644 --- a/src/eap_peer/eap_pax.c +++ b/src/eap_peer/eap_pax.c @@ -276,9 +276,16 @@ static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data, left -= 2; wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", pos, 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, mac); + 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, mac) < 0) { + wpa_printf(MSG_INFO, + "EAP-PAX: Could not derive MAC_CK(B, CID)"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + if (os_memcmp_const(pos, mac, EAP_PAX_MAC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) " "received"); @@ -306,9 +313,12 @@ static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data, /* Optional ADE could be added here, if needed */ rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN); - eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, - NULL, 0, NULL, 0, rpos); + if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, rpos) < 0) { + wpabuf_free(resp); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); data->state = PAX_DONE; @@ -472,9 +482,13 @@ static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) return NULL; *len = EAP_MSK_LEN; - eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, - "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, - EAP_MSK_LEN, key); + if (eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, + "Master Session Key", + data->rand.e, 2 * EAP_PAX_RAND_LEN, + EAP_MSK_LEN, key) < 0) { + os_free(key); + return NULL; + } return key; } @@ -493,10 +507,13 @@ static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) return NULL; *len = EAP_EMSK_LEN; - eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, - "Extended Master Session Key", - data->rand.e, 2 * EAP_PAX_RAND_LEN, - EAP_EMSK_LEN, key); + if (eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, + "Extended Master Session Key", + data->rand.e, 2 * EAP_PAX_RAND_LEN, + EAP_EMSK_LEN, key) < 0) { + os_free(key); + return NULL; + } return key; } @@ -525,7 +542,6 @@ static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_pax_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX"); @@ -540,8 +556,5 @@ int eap_peer_pax_register(void) eap->get_emsk = eap_pax_get_emsk; eap->getSessionId = eap_pax_get_session_id; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index 98a48a6cf5d31..45ba38168d4fc 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-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -59,6 +59,7 @@ struct eap_peap_data { size_t id_len; struct wpabuf *pending_phase2_req; + struct wpabuf *pending_resp; enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; int crypto_binding_used; u8 binding_nonce[32]; @@ -69,8 +70,8 @@ struct eap_peap_data { }; -static int eap_peap_parse_phase1(struct eap_peap_data *data, - const char *phase1) +static void eap_peap_parse_phase1(struct eap_peap_data *data, + const char *phase1) { const char *pos; @@ -125,8 +126,6 @@ static int eap_peap_parse_phase1(struct eap_peap_data *data, wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled"); } #endif /* EAP_TNC */ - - return 0; } @@ -144,11 +143,8 @@ static void * eap_peap_init(struct eap_sm *sm) data->peap_outer_success = 2; data->crypto_binding = OPTIONAL_BINDING; - if (config && config->phase1 && - eap_peap_parse_phase1(data, config->phase1) < 0) { - eap_peap_deinit(sm, data); - return NULL; - } + if (config && config->phase1) + eap_peap_parse_phase1(data, config->phase1); if (eap_peer_select_phase2_methods(config, "auth=", &data->phase2_types, @@ -191,6 +187,7 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv) eap_peap_free_key(data); os_free(data->session_id); wpabuf_free(data->pending_phase2_req); + wpabuf_free(data->pending_resp); os_free(data); } @@ -256,6 +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; /* * Tunnel key (TK) is the first 60 octets of the key generated by @@ -266,8 +264,12 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); - if (data->reauth && - tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { + resumed = tls_connection_resumed(sm->ssl_ctx, data->ssl.conn); + wpa_printf(MSG_DEBUG, + "EAP-PEAP: CMK derivation - reauth=%d resumed=%d phase2_eap_started=%d phase2_success=%d", + data->reauth, resumed, data->phase2_eap_started, + data->phase2_success); + if (data->reauth && !data->phase2_eap_started && resumed) { /* Fast-connect: IPMK|CMK = TK */ os_memcpy(data->ipmk, tk, 40); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK", @@ -337,7 +339,8 @@ static int eap_tlv_add_cryptobinding(struct eap_sm *sm, addr[0], len[0]); wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2", addr[1], len[1]); - hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac); + if (hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac) < 0) + return -1; wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN); data->crypto_binding_used = 1; @@ -648,6 +651,7 @@ static int eap_peap_phase2_request(struct eap_sm *sm, if (*resp == NULL) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; + wpabuf_free(buf); return -1; } wpabuf_put_buf(*resp, buf); @@ -1006,6 +1010,34 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, !data->resuming) { res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp); } else { + if (sm->waiting_ext_cert_check && data->pending_resp) { + struct eap_peer_config *config = eap_get_config(sm); + + if (config->pending_ext_cert_check == + EXT_CERT_CHECK_GOOD) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: External certificate check succeeded - continue handshake"); + resp = data->pending_resp; + data->pending_resp = NULL; + sm->waiting_ext_cert_check = 0; + return resp; + } + + if (config->pending_ext_cert_check == + EXT_CERT_CHECK_BAD) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: External certificate check failed - force authentication failure"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + sm->waiting_ext_cert_check = 0; + return NULL; + } + + wpa_printf(MSG_DEBUG, + "EAP-PEAP: Continuing to wait external server certificate validation"); + return NULL; + } + res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_PEAP, data->peap_version, id, &msg, @@ -1018,6 +1050,16 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, ret->decision = DECISION_FAIL; return resp; } + + + if (sm->waiting_ext_cert_check) { + wpa_printf(MSG_DEBUG, + "EAP-PEAP: Waiting external server certificate validation"); + wpabuf_free(data->pending_resp); + data->pending_resp = resp; + return NULL; + } + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { char *label; wpa_printf(MSG_DEBUG, @@ -1123,6 +1165,8 @@ static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) struct eap_peap_data *data = priv; wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = NULL; + wpabuf_free(data->pending_resp); + data->pending_resp = NULL; data->crypto_binding_used = 0; } @@ -1237,7 +1281,6 @@ static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_peap_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP"); @@ -1255,8 +1298,5 @@ int eap_peer_peap_register(void) eap->init_for_reauth = eap_peap_init_for_reauth; eap->getSessionId = eap_peap_get_session_id; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c index f01266354e904..ac18c158ad8bb 100644 --- a/src/eap_peer/eap_psk.c +++ b/src/eap_peer/eap_psk.c @@ -480,7 +480,6 @@ static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_psk_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK"); @@ -495,8 +494,5 @@ int eap_peer_psk_register(void) eap->getSessionId = eap_psk_get_session_id; eap->get_emsk = eap_psk_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c index 1f785443ee5ac..d2bc981cd06b8 100644 --- a/src/eap_peer/eap_pwd.c +++ b/src/eap_peer/eap_pwd.c @@ -418,7 +418,6 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail"); goto fin; } - BN_clear_free(mask); if (((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { @@ -555,6 +554,7 @@ fin: os_free(element); BN_clear_free(x); BN_clear_free(y); + BN_clear_free(mask); BN_clear_free(cofactor); EC_POINT_clear_free(K); EC_POINT_clear_free(point); @@ -774,7 +774,8 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); fin: - bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); + if (data->grp) + bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); BN_clear_free(x); BN_clear_free(y); if (data->outbuf == NULL) { @@ -903,7 +904,7 @@ 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)) { + if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) { data->in_frag_pos += len; if (data->in_frag_pos > wpabuf_size(data->inbuf)) { wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack " @@ -916,7 +917,8 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, return NULL; } wpabuf_put_data(data->inbuf, pos, len); - + } + if (EAP_PWD_GET_MORE_BIT(lm_exch)) { resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, EAP_PWD_HDR_SIZE, EAP_CODE_RESPONSE, eap_get_id(reqData)); @@ -930,10 +932,8 @@ 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) { - wpabuf_put_data(data->inbuf, pos, len); wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", (int) len); - data->in_frag_pos += len; pos = wpabuf_head_u8(data->inbuf); len = data->in_frag_pos; } @@ -1054,7 +1054,6 @@ static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_pwd_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD"); @@ -1069,8 +1068,5 @@ int eap_peer_pwd_register(void) eap->getSessionId = eap_pwd_get_session_id; eap->get_emsk = eap_pwd_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c index c4f9843febb35..330febbefd78c 100644 --- a/src/eap_peer/eap_sake.c +++ b/src/eap_peer/eap_sake.c @@ -309,11 +309,20 @@ static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm, return NULL; } - eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - data->serverid, data->serverid_len, - data->peerid, data->peerid_len, 0, - wpabuf_head(reqData), wpabuf_len(reqData), - attr.mic_s, mic_s); + if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len, 0, + wpabuf_head(reqData), wpabuf_len(reqData), + attr.mic_s, mic_s)) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); + eap_sake_state(data, FAILURE); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Auth-Reject"); + return eap_sake_build_msg(data, id, 0, + EAP_SAKE_SUBTYPE_AUTH_REJECT); + } if (os_memcmp_const(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) { wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S"); eap_sake_state(data, FAILURE); @@ -494,7 +503,6 @@ static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_sake_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE"); @@ -509,8 +517,5 @@ int eap_peer_sake_register(void) eap->getSessionId = eap_sake_get_session_id; eap->get_emsk = eap_sake_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c index 99a2816ce61ed..b97c95db196f0 100644 --- a/src/eap_peer/eap_sim.c +++ b/src/eap_peer/eap_sim.c @@ -249,6 +249,7 @@ static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) return eap_sim_ext_sim_req(sm, data); } +#ifdef PCSC_FUNCS if (conf->pcsc) { if (scard_gsm_auth(sm->scard_ctx, data->rand[0], data->sres[0], data->kc[0]) || @@ -263,6 +264,7 @@ static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) } return 0; } +#endif /* PCSC_FUNCS */ #ifdef CONFIG_SIM_SIMULATOR if (conf->password) { @@ -1135,7 +1137,7 @@ static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv) if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " "for NONCE_MT"); - os_free(data); + eap_sim_deinit(sm, data); return NULL; } data->num_id_req = 0; @@ -1235,7 +1237,6 @@ static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_sim_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM"); @@ -1254,8 +1255,5 @@ int eap_peer_sim_register(void) eap->get_identity = eap_sim_get_identity; eap->get_emsk = eap_sim_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c index 66a027a626e0d..ca2354f8a7850 100644 --- a/src/eap_peer/eap_tls.c +++ b/src/eap_peer/eap_tls.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-TLS (RFC 2716) - * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2008, 2012-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -25,6 +25,7 @@ struct eap_tls_data { size_t id_len; void *ssl_ctx; u8 eap_type; + struct wpabuf *pending_resp; }; @@ -142,6 +143,7 @@ static void eap_tls_deinit(struct eap_sm *sm, void *priv) eap_peer_tls_ssl_deinit(sm, &data->ssl); eap_tls_free_key(data); os_free(data->session_id); + wpabuf_free(data->pending_resp); os_free(data); } @@ -216,6 +218,32 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, struct eap_tls_data *data = priv; struct wpabuf msg; + if (sm->waiting_ext_cert_check && data->pending_resp) { + struct eap_peer_config *config = eap_get_config(sm); + + if (config->pending_ext_cert_check == EXT_CERT_CHECK_GOOD) { + wpa_printf(MSG_DEBUG, + "EAP-TLS: External certificate check succeeded - continue handshake"); + resp = data->pending_resp; + data->pending_resp = NULL; + sm->waiting_ext_cert_check = 0; + return resp; + } + + if (config->pending_ext_cert_check == EXT_CERT_CHECK_BAD) { + wpa_printf(MSG_DEBUG, + "EAP-TLS: External certificate check failed - force authentication failure"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + sm->waiting_ext_cert_check = 0; + return NULL; + } + + wpa_printf(MSG_DEBUG, + "EAP-TLS: Continuing to wait external server certificate validation"); + return NULL; + } + pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret, reqData, &left, &flags); if (pos == NULL) @@ -237,6 +265,14 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, return eap_tls_failure(sm, data, ret, res, resp, id); } + if (sm->waiting_ext_cert_check) { + wpa_printf(MSG_DEBUG, + "EAP-TLS: Waiting external server certificate validation"); + wpabuf_free(data->pending_resp); + data->pending_resp = resp; + return NULL; + } + if (tls_connection_established(data->ssl_ctx, data->ssl.conn)) eap_tls_success(sm, data, ret); @@ -258,6 +294,10 @@ static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv) static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv) { + struct eap_tls_data *data = priv; + + wpabuf_free(data->pending_resp); + data->pending_resp = NULL; } @@ -350,7 +390,6 @@ static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_tls_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS"); @@ -369,10 +408,7 @@ int eap_peer_tls_register(void) eap->init_for_reauth = eap_tls_init_for_reauth; eap->get_emsk = eap_tls_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } @@ -380,7 +416,6 @@ int eap_peer_tls_register(void) int eap_peer_unauth_tls_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_UNAUTH_TLS, @@ -399,10 +434,7 @@ int eap_peer_unauth_tls_register(void) eap->init_for_reauth = eap_tls_init_for_reauth; eap->get_emsk = eap_tls_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } #endif /* EAP_UNAUTH_TLS */ @@ -411,7 +443,6 @@ int eap_peer_unauth_tls_register(void) int eap_peer_wfa_unauth_tls_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_WFA_NEW, @@ -431,9 +462,6 @@ int eap_peer_wfa_unauth_tls_register(void) eap->init_for_reauth = eap_tls_init_for_reauth; eap->get_emsk = eap_tls_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } #endif /* CONFIG_HS20 */ diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index af2b7541d7016..0dcb9c138f81d 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -80,6 +80,10 @@ static void eap_tls_params_flags(struct tls_connection_params *params, params->flags |= TLS_CONN_DISABLE_TLSv1_2; if (os_strstr(txt, "tls_disable_tlsv1_2=0")) params->flags &= ~TLS_CONN_DISABLE_TLSv1_2; + if (os_strstr(txt, "tls_ext_cert_check=1")) + params->flags |= TLS_CONN_EXT_CERT_CHECK; + if (os_strstr(txt, "tls_ext_cert_check=0")) + params->flags &= ~TLS_CONN_EXT_CERT_CHECK; } @@ -177,6 +181,8 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, params->openssl_ciphers = config->openssl_ciphers; + sm->ext_cert_check = !!(params->flags & TLS_CONN_EXT_CERT_CHECK); + return 0; } @@ -190,8 +196,10 @@ static int eap_tls_init_connection(struct eap_sm *sm, if (config->ocsp) params->flags |= TLS_CONN_REQUEST_OCSP; - if (config->ocsp == 2) + if (config->ocsp >= 2) params->flags |= TLS_CONN_REQUIRE_OCSP; + if (config->ocsp == 3) + params->flags |= TLS_CONN_REQUIRE_OCSP_ALL; data->conn = tls_connection_init(data->ssl_ctx); if (data->conn == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " @@ -320,8 +328,8 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, if (out == NULL) return NULL; - if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, 0, - out, len)) { + if (tls_connection_export_key(data->ssl_ctx, data->conn, label, out, + len)) { os_free(out); return NULL; } @@ -350,10 +358,8 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, struct tls_random keys; u8 *out; - if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys)) - return NULL; - - if (keys.client_random == NULL || keys.server_random == NULL) + if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys) || + keys.client_random == NULL || keys.server_random == NULL) return NULL; *len = 1 + keys.client_random_len + keys.server_random_len; @@ -1035,6 +1041,9 @@ int eap_peer_select_phase2_methods(struct eap_peer_config *config, if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) { wpa_printf(MSG_ERROR, "TLS: Unsupported Phase2 EAP " "method '%s'", start); + os_free(methods); + os_free(buf); + return -1; } else { num_methods++; _methods = os_realloc_array(methods, num_methods, diff --git a/src/eap_peer/eap_tnc.c b/src/eap_peer/eap_tnc.c index 25b9f124801a1..726221e6b69ca 100644 --- a/src/eap_peer/eap_tnc.c +++ b/src/eap_peer/eap_tnc.c @@ -10,6 +10,7 @@ #include "common.h" #include "eap_i.h" +#include "eap_config.h" #include "tncc.h" @@ -35,12 +36,16 @@ struct eap_tnc_data { static void * eap_tnc_init(struct eap_sm *sm) { struct eap_tnc_data *data; + struct eap_peer_config *config = eap_get_config(sm); data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; data->state = WAIT_START; - data->fragment_size = 1300; + if (config && config->fragment_size) + data->fragment_size = config->fragment_size; + else + data->fragment_size = 1300; data->tncc = tncc_init(); if (data->tncc == NULL) { os_free(data); @@ -345,11 +350,6 @@ static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv, ret->decision = DECISION_UNCOND_SUCC; ret->allowNotifications = TRUE; - if (data->out_buf) { - data->state = PROC_MSG; - return eap_tnc_build_msg(data, ret, id); - } - if (tncs_done) { resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_RESPONSE, eap_get_id(reqData)); @@ -410,7 +410,6 @@ fail: int eap_peer_tnc_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC"); @@ -421,8 +420,5 @@ int eap_peer_tnc_register(void) eap->deinit = eap_tnc_deinit; eap->process = eap_tnc_process; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index b186c9156a741..92f94dcd60190 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-TTLS (RFC 5281) - * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -35,6 +35,7 @@ struct eap_ttls_data { void *phase2_priv; int phase2_success; int phase2_start; + EapDecision decision_succ; enum phase2_types { EAP_TTLS_PHASE2_EAP, @@ -58,6 +59,7 @@ struct eap_ttls_data { size_t id_len; struct wpabuf *pending_phase2_req; + struct wpabuf *pending_resp; #ifdef EAP_TNC int ready_for_tnc; @@ -70,6 +72,7 @@ static void * eap_ttls_init(struct eap_sm *sm) { struct eap_ttls_data *data; struct eap_peer_config *config = eap_get_config(sm); + int selected_non_eap; char *selected; data = os_zalloc(sizeof(*data)); @@ -77,26 +80,67 @@ static void * eap_ttls_init(struct eap_sm *sm) return NULL; data->ttls_version = EAP_TTLS_VERSION; selected = "EAP"; + selected_non_eap = 0; data->phase2_type = EAP_TTLS_PHASE2_EAP; + /* + * Either one auth= type or one or more autheap= methods can be + * specified. + */ if (config && config->phase2) { + const char *token, *last = NULL; + + while ((token = cstr_token(config->phase2, " \t", &last))) { + if (os_strncmp(token, "auth=", 5) != 0) + continue; + token += 5; + + if (last - token == 8 && + os_strncmp(token, "MSCHAPV2", 8) == 0) { + selected = "MSCHAPV2"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; + } else if (last - token == 6 && + os_strncmp(token, "MSCHAP", 6) == 0) { + selected = "MSCHAP"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAP; + } else if (last - token == 3 && + os_strncmp(token, "PAP", 3) == 0) { + selected = "PAP"; + data->phase2_type = EAP_TTLS_PHASE2_PAP; + } else if (last - token == 4 && + os_strncmp(token, "CHAP", 4) == 0) { + selected = "CHAP"; + data->phase2_type = EAP_TTLS_PHASE2_CHAP; + } else { + wpa_printf(MSG_ERROR, + "EAP-TTLS: Unsupported Phase2 type '%s'", + token); + eap_ttls_deinit(sm, data); + return NULL; + } + + if (selected_non_eap) { + wpa_printf(MSG_ERROR, + "EAP-TTLS: Only one Phase2 type can be specified"); + eap_ttls_deinit(sm, data); + return NULL; + } + + selected_non_eap = 1; + } + if (os_strstr(config->phase2, "autheap=")) { + if (selected_non_eap) { + wpa_printf(MSG_ERROR, + "EAP-TTLS: Both auth= and autheap= params cannot be specified"); + eap_ttls_deinit(sm, data); + return NULL; + } selected = "EAP"; data->phase2_type = EAP_TTLS_PHASE2_EAP; - } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) { - selected = "MSCHAPV2"; - data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; - } else if (os_strstr(config->phase2, "auth=MSCHAP")) { - selected = "MSCHAP"; - data->phase2_type = EAP_TTLS_PHASE2_MSCHAP; - } else if (os_strstr(config->phase2, "auth=PAP")) { - selected = "PAP"; - data->phase2_type = EAP_TTLS_PHASE2_PAP; - } else if (os_strstr(config->phase2, "auth=CHAP")) { - selected = "CHAP"; - data->phase2_type = EAP_TTLS_PHASE2_CHAP; } } + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected); if (data->phase2_type == EAP_TTLS_PHASE2_EAP) { @@ -153,6 +197,7 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv) eap_ttls_free_key(data); os_free(data->session_id); wpabuf_free(data->pending_phase2_req); + wpabuf_free(data->pending_resp); os_free(data); } @@ -1408,6 +1453,32 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, { int res; + if (sm->waiting_ext_cert_check && data->pending_resp) { + struct eap_peer_config *config = eap_get_config(sm); + + if (config->pending_ext_cert_check == EXT_CERT_CHECK_GOOD) { + wpa_printf(MSG_DEBUG, + "EAP-TTLS: External certificate check succeeded - continue handshake"); + *out_data = data->pending_resp; + data->pending_resp = NULL; + sm->waiting_ext_cert_check = 0; + return 0; + } + + if (config->pending_ext_cert_check == EXT_CERT_CHECK_BAD) { + wpa_printf(MSG_DEBUG, + "EAP-TTLS: External certificate check failed - force authentication failure"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + sm->waiting_ext_cert_check = 0; + return 0; + } + + wpa_printf(MSG_DEBUG, + "EAP-TTLS: Continuing to wait external server certificate validation"); + return 0; + } + res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, data->ttls_version, identifier, in_data, out_data); @@ -1418,6 +1489,15 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, return -1; } + if (sm->waiting_ext_cert_check) { + wpa_printf(MSG_DEBUG, + "EAP-TTLS: Waiting external server certificate validation"); + wpabuf_free(data->pending_resp); + data->pending_resp = *out_data; + *out_data = NULL; + return 0; + } + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to " "Phase 2"); @@ -1468,6 +1548,7 @@ static void eap_ttls_check_auth_status(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " "completed successfully"); data->phase2_success = 1; + data->decision_succ = ret->decision; #ifdef EAP_TNC if (!data->ready_for_tnc && !data->tnc_started) { /* @@ -1485,6 +1566,18 @@ static void eap_ttls_check_auth_status(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " "completed successfully (MAY_CONT)"); data->phase2_success = 1; + data->decision_succ = ret->decision; + } else if (data->decision_succ != DECISION_FAIL && + data->phase2_success && + !data->ssl.tls_out) { + /* + * This is needed to cover the case where the final Phase 2 + * message gets fragmented since fragmentation clears + * decision back to FAIL. + */ + wpa_printf(MSG_DEBUG, + "EAP-TTLS: Restore success decision after fragmented frame sent completely"); + ret->decision = data->decision_succ; } } @@ -1557,6 +1650,9 @@ static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) struct eap_ttls_data *data = priv; wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = NULL; + wpabuf_free(data->pending_resp); + data->pending_resp = NULL; + data->decision_succ = DECISION_FAIL; #ifdef EAP_TNC data->ready_for_tnc = 0; data->tnc_started = 0; @@ -1695,7 +1791,6 @@ static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_ttls_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); @@ -1714,8 +1809,5 @@ int eap_peer_ttls_register(void) eap->init_for_reauth = eap_ttls_init_for_reauth; eap->get_emsk = eap_ttls_get_emsk; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_vendor_test.c b/src/eap_peer/eap_vendor_test.c index b61057ee67555..16e3c39563b72 100644 --- a/src/eap_peer/eap_vendor_test.c +++ b/src/eap_peer/eap_vendor_test.c @@ -169,7 +169,6 @@ static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len) int eap_peer_vendor_test_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_ID, EAP_VENDOR_TYPE, @@ -183,8 +182,5 @@ int eap_peer_vendor_test_register(void) eap->isKeyAvailable = eap_vendor_test_isKeyAvailable; eap->getKey = eap_vendor_test_getKey; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c index 7ac99c7ce727b..d140c88b8cdd8 100644 --- a/src/eap_peer/eap_wsc.c +++ b/src/eap_peer/eap_wsc.c @@ -17,7 +17,7 @@ struct eap_wsc_data { - enum { WAIT_START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; + enum { WAIT_START, MESG, WAIT_FRAG_ACK, FAIL } state; int registrar; struct wpabuf *in_buf; struct wpabuf *out_buf; @@ -36,12 +36,8 @@ static const char * eap_wsc_state_txt(int state) return "WAIT_START"; case MESG: return "MESG"; - case FRAG_ACK: - return "FRAG_ACK"; case WAIT_FRAG_ACK: return "WAIT_FRAG_ACK"; - case DONE: - return "DONE"; case FAIL: return "FAIL"; default: @@ -579,7 +575,6 @@ send_msg: int eap_peer_wsc_register(void) { struct eap_method *eap; - int ret; eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, @@ -591,8 +586,5 @@ int eap_peer_wsc_register(void) eap->deinit = eap_wsc_deinit; eap->process = eap_wsc_process; - ret = eap_peer_method_register(eap); - if (ret) - eap_peer_method_free(eap); - return ret; + return eap_peer_method_register(eap); } diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c index 55ab72aee66c7..ca6502ea02e73 100644 --- a/src/eap_peer/ikev2.c +++ b/src/eap_peer/ikev2.c @@ -128,7 +128,7 @@ static int ikev2_parse_transform(struct ikev2_proposal_data *prop, t = (const struct ikev2_transform *) pos; transform_len = WPA_GET_BE16(t->transform_length); - if (transform_len < (int) sizeof(*t) || pos + transform_len > end) { + if (transform_len < (int) sizeof(*t) || transform_len > end - pos) { wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d", transform_len); return -1; @@ -248,7 +248,7 @@ static int ikev2_parse_proposal(struct ikev2_proposal_data *prop, ppos = (const u8 *) (p + 1); pend = pos + proposal_len; - if (ppos + p->spi_size > pend) { + if (p->spi_size > pend - ppos) { wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI " "in proposal"); return -1; diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c index 7ca956e5b235d..0c5caa7dd522e 100644 --- a/src/eap_peer/tncc.c +++ b/src/eap_peer/tncc.c @@ -104,7 +104,7 @@ static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL }; /* TNCC functions that IMCs can call */ -TNC_Result TNC_TNCC_ReportMessageTypes( +static TNC_Result TNC_TNCC_ReportMessageTypes( TNC_IMCID imcID, TNC_MessageTypeList supportedTypes, TNC_UInt32 typeCount) @@ -138,7 +138,7 @@ TNC_Result TNC_TNCC_ReportMessageTypes( } -TNC_Result TNC_TNCC_SendMessage( +static TNC_Result TNC_TNCC_SendMessage( TNC_IMCID imcID, TNC_ConnectionID connectionID, TNC_BufferReference message, @@ -183,7 +183,7 @@ TNC_Result TNC_TNCC_SendMessage( } -TNC_Result TNC_TNCC_RequestHandshakeRetry( +static TNC_Result TNC_TNCC_RequestHandshakeRetry( TNC_IMCID imcID, TNC_ConnectionID connectionID, TNC_RetryReason reason) @@ -203,8 +203,8 @@ TNC_Result TNC_TNCC_RequestHandshakeRetry( } -TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity, - const char *message) +static TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity, + const char *message) { wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu " "severity==%lu message='%s')", @@ -213,8 +213,9 @@ TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity, } -TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID, - const char *message) +static TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, + TNC_ConnectionID connectionID, + const char *message) { wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu " "connectionID==%lu message='%s')", @@ -223,7 +224,7 @@ TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID, } -TNC_Result TNC_TNCC_BindFunction( +static TNC_Result TNC_TNCC_BindFunction( TNC_IMCID imcID, char *functionName, void **pOutfunctionPointer) @@ -694,6 +695,8 @@ enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc, enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION; int recommendation_msg = 0; + wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Received IF-TNCCS message", + msg, len); buf = dup_binstr(msg, len); if (buf == NULL) return TNCCS_PROCESS_ERROR; diff --git a/src/eap_server/eap_methods.h b/src/eap_server/eap_methods.h index 0baa3279086e4..3bf1495f76bf0 100644 --- a/src/eap_server/eap_methods.h +++ b/src/eap_server/eap_methods.h @@ -15,7 +15,6 @@ const struct eap_method * eap_server_get_eap_method(int vendor, EapType method); struct eap_method * eap_server_method_alloc(int version, int vendor, EapType method, const char *name); -void eap_server_method_free(struct eap_method *method); int eap_server_method_register(struct eap_method *method); EapType eap_server_get_type(const char *name, int *vendor); diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c index db9b6aa2db39a..a8bb5eae6b56a 100644 --- a/src/eap_server/eap_server_aka.c +++ b/src/eap_server/eap_server_aka.c @@ -1319,7 +1319,6 @@ static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_aka_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); @@ -1337,10 +1336,7 @@ int eap_server_aka_register(void) eap->get_emsk = eap_aka_get_emsk; eap->getSessionId = eap_aka_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } @@ -1348,7 +1344,6 @@ int eap_server_aka_register(void) int eap_server_aka_prime_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, @@ -1367,10 +1362,6 @@ int eap_server_aka_prime_register(void) eap->get_emsk = eap_aka_get_emsk; eap->getSessionId = eap_aka_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - - return ret; + return eap_server_method_register(eap); } #endif /* EAP_SERVER_AKA_PRIME */ diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c index ba82be9c3f3ad..1eba8f515648a 100644 --- a/src/eap_server/eap_server_eke.c +++ b/src/eap_server/eap_server_eke.c @@ -792,7 +792,6 @@ static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_eke_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); @@ -810,8 +809,5 @@ int eap_server_eke_register(void) eap->get_emsk = eap_eke_get_emsk; eap->getSessionId = eap_eke_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c index bd9018e78b56b..20491726880e9 100644 --- a/src/eap_server/eap_server_fast.c +++ b/src/eap_server/eap_server_fast.c @@ -180,42 +180,47 @@ static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, buf, end - buf); pos = buf; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + u8 id, elen; + + id = *pos++; + elen = *pos++; + if (elen > end - pos) break; - switch (*pos) { + switch (id) { case PAC_OPAQUE_TYPE_PAD: goto done; case PAC_OPAQUE_TYPE_KEY: - if (pos[1] != EAP_FAST_PAC_KEY_LEN) { - wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid " - "PAC-Key length %d", pos[1]); + if (elen != EAP_FAST_PAC_KEY_LEN) { + wpa_printf(MSG_DEBUG, + "EAP-FAST: Invalid PAC-Key length %d", + elen); os_free(buf); return -1; } - pac_key = pos + 2; + pac_key = pos; wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key from " "decrypted PAC-Opaque", pac_key, EAP_FAST_PAC_KEY_LEN); break; case PAC_OPAQUE_TYPE_LIFETIME: - if (pos[1] != 4) { + if (elen != 4) { wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid " "PAC-Key lifetime length %d", - pos[1]); + elen); os_free(buf); return -1; } - lifetime = WPA_GET_BE32(pos + 2); + lifetime = WPA_GET_BE32(pos); break; case PAC_OPAQUE_TYPE_IDENTITY: - identity = pos + 2; - identity_len = pos[1]; + identity = pos; + identity_len = elen; break; } - pos += 2 + pos[1]; + pos += elen; } done: @@ -273,7 +278,7 @@ static void eap_fast_derive_key_auth(struct eap_sm *sm, * Extra key material after TLS key_block: session_key_seed[40] */ - sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion", + sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, EAP_FAST_SKS_LEN); if (sks == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " @@ -300,7 +305,6 @@ static void eap_fast_derive_key_provisioning(struct eap_sm *sm, os_free(data->key_block_p); data->key_block_p = (struct eap_fast_key_block_provisioning *) eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, - "key expansion", sizeof(*data->key_block_p)); if (data->key_block_p == NULL) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block"); @@ -407,11 +411,13 @@ static int eap_fast_update_icmk(struct eap_sm *sm, struct eap_fast_data *data) static void * eap_fast_init(struct eap_sm *sm) { struct eap_fast_data *data; - u8 ciphers[5] = { + u8 ciphers[7] = { TLS_CIPHER_ANON_DH_AES128_SHA, TLS_CIPHER_AES128_SHA, TLS_CIPHER_RSA_DHE_AES128_SHA, TLS_CIPHER_RC4_SHA, + TLS_CIPHER_RSA_DHE_AES256_SHA, + TLS_CIPHER_AES256_SHA, TLS_CIPHER_NONE }; @@ -1134,7 +1140,7 @@ static int eap_fast_parse_tlvs(struct wpabuf *data, pos = wpabuf_mhead(data); end = pos + wpabuf_len(data); - while (pos + 4 < end) { + while (end - pos > 4) { mandatory = pos[0] & 0x80; tlv_type = WPA_GET_BE16(pos) & 0x3fff; pos += 2; @@ -1559,7 +1565,10 @@ static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len) if (eapKeyData == NULL) return NULL; - eap_fast_derive_eap_msk(data->simck, eapKeyData); + if (eap_fast_derive_eap_msk(data->simck, eapKeyData) < 0) { + os_free(eapKeyData); + return NULL; + } *len = EAP_FAST_KEY_LEN; return eapKeyData; @@ -1578,7 +1587,10 @@ static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (eapKeyData == NULL) return NULL; - eap_fast_derive_eap_emsk(data->simck, eapKeyData); + if (eap_fast_derive_eap_emsk(data->simck, eapKeyData) < 0) { + os_free(eapKeyData); + return NULL; + } *len = EAP_EMSK_LEN; return eapKeyData; @@ -1607,7 +1619,6 @@ static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_fast_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST"); @@ -1625,8 +1636,5 @@ int eap_server_fast_register(void) eap->isSuccess = eap_fast_isSuccess; eap->getSessionId = eap_fast_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c index 50f15c31d0dc0..94e74ec9b2f7d 100644 --- a/src/eap_server/eap_server_gpsk.c +++ b/src/eap_server/eap_server_gpsk.c @@ -631,7 +631,6 @@ static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_gpsk_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK"); @@ -649,8 +648,5 @@ int eap_server_gpsk_register(void) eap->get_emsk = eap_gpsk_get_emsk; eap->getSessionId = eap_gpsk_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_gtc.c b/src/eap_server/eap_server_gtc.c index 98ac3c6ec4958..193a8517ac086 100644 --- a/src/eap_server/eap_server_gtc.c +++ b/src/eap_server/eap_server_gtc.c @@ -202,7 +202,6 @@ static Boolean eap_gtc_isSuccess(struct eap_sm *sm, void *priv) int eap_server_gtc_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC"); @@ -217,8 +216,5 @@ int eap_server_gtc_register(void) eap->isDone = eap_gtc_isDone; eap->isSuccess = eap_gtc_isSuccess; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_identity.c b/src/eap_server/eap_server_identity.c index 45015336b9079..1b1db53f25b20 100644 --- a/src/eap_server/eap_server_identity.c +++ b/src/eap_server/eap_server_identity.c @@ -157,7 +157,6 @@ static Boolean eap_identity_isSuccess(struct eap_sm *sm, void *priv) int eap_server_identity_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, @@ -174,8 +173,5 @@ int eap_server_identity_register(void) eap->isDone = eap_identity_isDone; eap->isSuccess = eap_identity_isSuccess; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c index 16e62764cc55a..3a249d141e0cf 100644 --- a/src/eap_server/eap_server_ikev2.c +++ b/src/eap_server/eap_server_ikev2.c @@ -550,7 +550,6 @@ static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_ikev2_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_IKEV2, @@ -569,8 +568,5 @@ int eap_server_ikev2_register(void) eap->get_emsk = eap_ikev2_get_emsk; eap->getSessionId = eap_ikev2_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_md5.c b/src/eap_server/eap_server_md5.c index 71e8d59e03960..cf5ceb1d15298 100644 --- a/src/eap_server/eap_server_md5.c +++ b/src/eap_server/eap_server_md5.c @@ -153,7 +153,6 @@ static Boolean eap_md5_isSuccess(struct eap_sm *sm, void *priv) int eap_server_md5_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5"); @@ -168,8 +167,5 @@ int eap_server_md5_register(void) eap->isDone = eap_md5_isDone; eap->isSuccess = eap_md5_isSuccess; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_methods.c b/src/eap_server/eap_server_methods.c index 9e9dc934eb776..79ed3447ac0a2 100644 --- a/src/eap_server/eap_server_methods.c +++ b/src/eap_server/eap_server_methods.c @@ -87,7 +87,7 @@ struct eap_method * eap_server_method_alloc(int version, int vendor, * eap_server_method_free - Free EAP server method structure * @method: Method structure allocated with eap_server_method_alloc() */ -void eap_server_method_free(struct eap_method *method) +static void eap_server_method_free(struct eap_method *method) { os_free(method); } @@ -95,26 +95,31 @@ void eap_server_method_free(struct eap_method *method) /** * eap_server_method_register - Register an EAP server method - * @method: EAP method to register + * @method: EAP method to register from eap_server_method_alloc() * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method * has already been registered * * Each EAP server method needs to call this function to register itself as a - * supported EAP method. + * supported EAP method. The caller must not free the allocated method data + * regardless of the return value. */ int eap_server_method_register(struct eap_method *method) { struct eap_method *m, *last = NULL; if (method == NULL || method->name == NULL || - method->version != EAP_SERVER_METHOD_INTERFACE_VERSION) + method->version != EAP_SERVER_METHOD_INTERFACE_VERSION) { + eap_server_method_free(method); return -1; + } for (m = eap_methods; m; m = m->next) { if ((m->vendor == method->vendor && m->method == method->method) || - os_strcmp(m->name, method->name) == 0) + os_strcmp(m->name, method->name) == 0) { + eap_server_method_free(method); return -2; + } last = m; } diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c index 98d74e0d717e2..460cd9c82ff56 100644 --- a/src/eap_server/eap_server_mschapv2.c +++ b/src/eap_server/eap_server_mschapv2.c @@ -571,7 +571,6 @@ static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) int eap_server_mschapv2_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, @@ -588,8 +587,5 @@ int eap_server_mschapv2_register(void) eap->getKey = eap_mschapv2_getKey; eap->isSuccess = eap_mschapv2_isSuccess; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c index 0e6b4a0698edc..782b8c316537e 100644 --- a/src/eap_server/eap_server_pax.c +++ b/src/eap_server/eap_server_pax.c @@ -565,7 +565,6 @@ static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_pax_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX"); @@ -583,8 +582,5 @@ int eap_server_pax_register(void) eap->get_emsk = eap_pax_get_emsk; eap->getSessionId = eap_pax_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c index 51062b0987e45..18d31b527fdd7 100644 --- a/src/eap_server/eap_server_peap.c +++ b/src/eap_server/eap_server_peap.c @@ -335,6 +335,18 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); + if (tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { + /* Fast-connect: IPMK|CMK = TK */ + os_memcpy(data->ipmk, tk, 40); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK", + data->ipmk, 40); + os_memcpy(data->cmk, tk + 40, 20); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK", + data->cmk, 20); + os_free(tk); + return 0; + } + eap_peap_get_isk(data, isk, sizeof(isk)); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk)); @@ -357,7 +369,6 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) os_free(tk); - /* TODO: fast-connect: IPMK|CMK = TK */ os_memcpy(data->ipmk, imck, 40); wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); os_memcpy(data->cmk, imck + 40, 20); @@ -1267,8 +1278,9 @@ static void eap_peap_process(struct eap_sm *sm, void *priv, wpa_printf(MSG_DEBUG, "EAP-PEAP: Resuming previous session - skip Phase2"); - eap_peap_state(data, SUCCESS_REQ); - tls_connection_set_success_data_resumed(data->ssl.conn); + eap_peap_req_success(sm, data); + if (data->state == SUCCESS_REQ) + tls_connection_set_success_data_resumed(data->ssl.conn); } @@ -1351,7 +1363,6 @@ static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_peap_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP"); @@ -1368,8 +1379,5 @@ int eap_server_peap_register(void) eap->isSuccess = eap_peap_isSuccess; eap->getSessionId = eap_peap_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c index 12b5d25d67ffc..857d421393bc8 100644 --- a/src/eap_server/eap_server_psk.c +++ b/src/eap_server/eap_server_psk.c @@ -510,7 +510,6 @@ static u8 * eap_psk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_psk_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK"); @@ -528,8 +527,5 @@ int eap_server_psk_register(void) eap->get_emsk = eap_psk_get_emsk; eap->getSessionId = eap_psk_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c index cb83ff7305bd0..64bf708e039a4 100644 --- a/src/eap_server/eap_server_pwd.c +++ b/src/eap_server/eap_server_pwd.c @@ -178,8 +178,13 @@ static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, return; } - /* an lfsr is good enough to generate unpredictable tokens */ - data->token = os_random(); + if (os_get_random((u8 *) &data->token, sizeof(data->token)) < 0) { + wpabuf_free(data->outbuf); + data->outbuf = NULL; + eap_pwd_state(data, FAILURE); + return; + } + wpabuf_put_be16(data->outbuf, data->group_num); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); @@ -970,7 +975,7 @@ 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)) { + if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) { if ((data->in_frag_pos + len) > wpabuf_size(data->inbuf)) { wpa_printf(MSG_DEBUG, "EAP-pwd: Buffer overflow " "attack detected! (%d+%d > %d)", @@ -981,6 +986,8 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv, } wpabuf_put_data(data->inbuf, pos, len); data->in_frag_pos += len; + } + if (EAP_PWD_GET_MORE_BIT(lm_exch)) { wpa_printf(MSG_DEBUG, "EAP-pwd: Got a %d byte fragment", (int) len); return; @@ -990,8 +997,6 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv, * buffering fragments so that's how we know it's the last) */ if (data->in_frag_pos) { - wpabuf_put_data(data->inbuf, pos, len); - data->in_frag_pos += len; pos = wpabuf_head_u8(data->inbuf); len = data->in_frag_pos; wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", @@ -1094,7 +1099,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; - int ret; struct timeval tp; struct timezone tz; u32 sr; @@ -1121,9 +1125,6 @@ int eap_server_pwd_register(void) eap->isSuccess = eap_pwd_is_success; eap->getSessionId = eap_pwd_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c index de70777318991..84d0e0be4dd12 100644 --- a/src/eap_server/eap_server_sake.c +++ b/src/eap_server/eap_server_sake.c @@ -520,7 +520,6 @@ static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_sake_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE"); @@ -538,8 +537,5 @@ int eap_server_sake_register(void) eap->get_emsk = eap_sake_get_emsk; eap->getSessionId = eap_sake_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c index ddfb71cf4e2ea..3a6ed795c7680 100644 --- a/src/eap_server/eap_server_sim.c +++ b/src/eap_server/eap_server_sim.c @@ -846,7 +846,6 @@ static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_sim_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM"); @@ -864,8 +863,5 @@ int eap_server_sim_register(void) eap->get_emsk = eap_sim_get_emsk; eap->getSessionId = eap_sim_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c index bd18a4ba654ce..7249858844ef2 100644 --- a/src/eap_server/eap_server_tls.c +++ b/src/eap_server/eap_server_tls.c @@ -375,7 +375,6 @@ static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) int eap_server_tls_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS"); @@ -393,10 +392,7 @@ int eap_server_tls_register(void) eap->get_emsk = eap_tls_get_emsk; eap->getSessionId = eap_tls_get_session_id; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } @@ -404,7 +400,6 @@ int eap_server_tls_register(void) int eap_server_unauth_tls_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_UNAUTH_TLS, @@ -423,10 +418,7 @@ int eap_server_unauth_tls_register(void) eap->isSuccess = eap_tls_isSuccess; eap->get_emsk = eap_tls_get_emsk; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } #endif /* EAP_SERVER_UNAUTH_TLS */ @@ -435,7 +427,6 @@ int eap_server_unauth_tls_register(void) int eap_server_wfa_unauth_tls_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_WFA_NEW, @@ -454,9 +445,6 @@ int eap_server_wfa_unauth_tls_register(void) eap->isSuccess = eap_tls_isSuccess; eap->get_emsk = eap_tls_get_emsk; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } #endif /* CONFIG_HS20 */ diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c index 05677b70e8873..69096954b8262 100644 --- a/src/eap_server/eap_server_tls_common.c +++ b/src/eap_server/eap_server_tls_common.c @@ -115,8 +115,8 @@ u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, if (out == NULL) return NULL; - if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, 0, - out, len)) { + if (tls_connection_export_key(sm->ssl_ctx, data->conn, label, out, + len)) { os_free(out); return NULL; } diff --git a/src/eap_server/eap_server_tnc.c b/src/eap_server/eap_server_tnc.c index 21bd26f8296ef..b568558fd42e1 100644 --- a/src/eap_server/eap_server_tnc.c +++ b/src/eap_server/eap_server_tnc.c @@ -554,7 +554,6 @@ static Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv) int eap_server_tnc_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC"); @@ -569,8 +568,5 @@ int eap_server_tnc_register(void) eap->isDone = eap_tnc_isDone; eap->isSuccess = eap_tnc_isSuccess; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c index 53ffa1ec6785a..a53633f8f1fe0 100644 --- a/src/eap_server/eap_server_ttls.c +++ b/src/eap_server/eap_server_ttls.c @@ -1335,7 +1335,6 @@ static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) int eap_server_ttls_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); @@ -1353,8 +1352,5 @@ int eap_server_ttls_register(void) eap->getSessionId = eap_ttls_get_session_id; eap->get_emsk = eap_ttls_get_emsk; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_vendor_test.c b/src/eap_server/eap_server_vendor_test.c index 30f600d3baf63..96399775945b8 100644 --- a/src/eap_server/eap_server_vendor_test.c +++ b/src/eap_server/eap_server_vendor_test.c @@ -168,7 +168,6 @@ static Boolean eap_vendor_test_isSuccess(struct eap_sm *sm, void *priv) int eap_server_vendor_test_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_ID, EAP_VENDOR_TYPE, @@ -185,8 +184,5 @@ int eap_server_vendor_test_register(void) eap->getKey = eap_vendor_test_getKey; eap->isSuccess = eap_vendor_test_isSuccess; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c index 9d9c28d704c10..7d9d285c39d01 100644 --- a/src/eap_server/eap_server_wsc.c +++ b/src/eap_server/eap_server_wsc.c @@ -488,7 +488,6 @@ static int eap_wsc_getTimeout(struct eap_sm *sm, void *priv) int eap_server_wsc_register(void) { struct eap_method *eap; - int ret; eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, @@ -505,8 +504,5 @@ int eap_server_wsc_register(void) eap->isSuccess = eap_wsc_isSuccess; eap->getTimeout = eap_wsc_getTimeout; - ret = eap_server_method_register(eap); - if (ret) - eap_server_method_free(eap); - return ret; + return eap_server_method_register(eap); } diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c index acf5435300dc5..d84c3d27de6be 100644 --- a/src/eap_server/eap_sim_db.c +++ b/src/eap_server/eap_sim_db.c @@ -66,6 +66,7 @@ struct eap_sim_db_data { struct eap_sim_pseudonym *pseudonyms; struct eap_sim_reauth *reauths; struct eap_sim_db_pending *pending; + unsigned int eap_sim_db_timeout; #ifdef CONFIG_SQLITE sqlite3 *sqlite_db; char db_tmp_identity[100]; @@ -76,6 +77,10 @@ struct eap_sim_db_data { }; +static void eap_sim_db_del_timeout(void *eloop_ctx, void *user_ctx); +static void eap_sim_db_query_timeout(void *eloop_ctx, void *user_ctx); + + #ifdef CONFIG_SQLITE static int db_table_exists(sqlite3 *db, const char *name) @@ -397,6 +402,57 @@ static void eap_sim_db_add_pending(struct eap_sim_db_data *data, } +static void eap_sim_db_free_pending(struct eap_sim_db_data *data, + struct eap_sim_db_pending *entry) +{ + eloop_cancel_timeout(eap_sim_db_query_timeout, data, entry); + eloop_cancel_timeout(eap_sim_db_del_timeout, data, entry); + os_free(entry); +} + + +static void eap_sim_db_del_pending(struct eap_sim_db_data *data, + struct eap_sim_db_pending *entry) +{ + struct eap_sim_db_pending **pp = &data->pending; + + while (*pp != NULL) { + if (*pp == entry) { + *pp = entry->next; + eap_sim_db_free_pending(data, entry); + return; + } + pp = &(*pp)->next; + } +} + + +static void eap_sim_db_del_timeout(void *eloop_ctx, void *user_ctx) +{ + struct eap_sim_db_data *data = eloop_ctx; + struct eap_sim_db_pending *entry = user_ctx; + + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Delete query timeout for %p", entry); + eap_sim_db_del_pending(data, entry); +} + + +static void eap_sim_db_query_timeout(void *eloop_ctx, void *user_ctx) +{ + struct eap_sim_db_data *data = eloop_ctx; + struct eap_sim_db_pending *entry = user_ctx; + + /* + * Report failure and allow some time for EAP server to process it + * before deleting the query. + */ + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Query timeout for %p", entry); + entry->state = FAILURE; + data->get_complete_cb(data->ctx, entry->cb_session_ctx); + eloop_register_timeout(1, 0, eap_sim_db_del_timeout, data, entry); +} + + static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, const char *imsi, char *buf) { @@ -472,7 +528,7 @@ static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, parse_fail: wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); - os_free(entry); + eap_sim_db_free_pending(data, entry); } @@ -563,7 +619,7 @@ static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, parse_fail: wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); - os_free(entry); + eap_sim_db_free_pending(data, entry); } @@ -690,12 +746,13 @@ static void eap_sim_db_close_socket(struct eap_sim_db_data *data) /** * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface * @config: Configuration data (e.g., file name) + * @db_timeout: Database lookup timeout * @get_complete_cb: Callback function for reporting availability of triplets * @ctx: Context pointer for get_complete_cb * Returns: Pointer to a private data structure or %NULL on failure */ struct eap_sim_db_data * -eap_sim_db_init(const char *config, +eap_sim_db_init(const char *config, unsigned int db_timeout, void (*get_complete_cb)(void *ctx, void *session_ctx), void *ctx) { @@ -709,6 +766,7 @@ eap_sim_db_init(const char *config, data->sock = -1; data->get_complete_cb = get_complete_cb; data->ctx = ctx; + data->eap_sim_db_timeout = db_timeout; data->fname = os_strdup(config); if (data->fname == NULL) goto fail; @@ -796,7 +854,7 @@ void eap_sim_db_deinit(void *priv) while (pending) { prev_pending = pending; pending = pending->next; - os_free(prev_pending); + eap_sim_db_free_pending(data, prev_pending); } os_free(data); @@ -833,11 +891,11 @@ static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg, } -static void eap_sim_db_expire_pending(struct eap_sim_db_data *data) +static void eap_sim_db_expire_pending(struct eap_sim_db_data *data, + struct eap_sim_db_pending *entry) { - /* TODO: add limit for maximum length for pending list; remove latest - * (i.e., last) entry from the list if the limit is reached; could also - * use timeout to expire pending entries */ + eloop_register_timeout(data->eap_sim_db_timeout, 0, + eap_sim_db_query_timeout, data, entry); } @@ -891,7 +949,7 @@ int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, if (entry->state == FAILURE) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " "failure"); - os_free(entry); + eap_sim_db_free_pending(data, entry); return EAP_SIM_DB_FAILURE; } @@ -911,7 +969,7 @@ int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, os_memcpy(sres, entry->u.sim.sres, num_chal * EAP_SIM_SRES_LEN); os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN); - os_free(entry); + eap_sim_db_free_pending(data, entry); return num_chal; } @@ -945,7 +1003,8 @@ int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); - eap_sim_db_expire_pending(data); + eap_sim_db_expire_pending(data, entry); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added query %p", entry); return EAP_SIM_DB_PENDING; } @@ -1356,7 +1415,7 @@ int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, entry = eap_sim_db_get_pending(data, imsi, 1); if (entry) { if (entry->state == FAILURE) { - os_free(entry); + eap_sim_db_free_pending(data, entry); wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure"); return EAP_SIM_DB_FAILURE; } @@ -1375,7 +1434,7 @@ int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN); os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN); *res_len = entry->u.aka.res_len; - os_free(entry); + eap_sim_db_free_pending(data, entry); return 0; } @@ -1406,7 +1465,8 @@ int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); - eap_sim_db_expire_pending(data); + eap_sim_db_expire_pending(data, entry); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added query %p", entry); return EAP_SIM_DB_PENDING; } diff --git a/src/eap_server/eap_sim_db.h b/src/eap_server/eap_sim_db.h index 53a1a7c3b4e30..ca900b9158523 100644 --- a/src/eap_server/eap_sim_db.h +++ b/src/eap_server/eap_sim_db.h @@ -31,7 +31,7 @@ enum eap_sim_db_method { struct eap_sim_db_data; struct eap_sim_db_data * -eap_sim_db_init(const char *config, +eap_sim_db_init(const char *config, unsigned int db_timeout, void (*get_complete_cb)(void *ctx, void *session_ctx), void *ctx); diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c index 632598fac72ae..5385cd89246fc 100644 --- a/src/eap_server/ikev2.c +++ b/src/eap_server/ikev2.c @@ -133,7 +133,7 @@ static int ikev2_parse_transform(struct ikev2_initiator_data *data, t = (const struct ikev2_transform *) pos; transform_len = WPA_GET_BE16(t->transform_length); - if (transform_len < (int) sizeof(*t) || pos + transform_len > end) { + if (transform_len < (int) sizeof(*t) || transform_len > end - pos) { wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d", transform_len); return -1; @@ -221,7 +221,7 @@ static int ikev2_parse_proposal(struct ikev2_initiator_data *data, p = (const struct ikev2_proposal *) pos; proposal_len = WPA_GET_BE16(p->proposal_length); - if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) { + if (proposal_len < (int) sizeof(*p) || proposal_len > end - pos) { wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d", proposal_len); return -1; @@ -256,7 +256,7 @@ static int ikev2_parse_proposal(struct ikev2_initiator_data *data, ppos = (const u8 *) (p + 1); pend = pos + proposal_len; - if (ppos + p->spi_size > pend) { + if (p->spi_size > pend - ppos) { wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI " "in proposal"); return -1; diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c index dc6f689c0b5eb..cfcbd3ed828c6 100644 --- a/src/eap_server/tncs.c +++ b/src/eap_server/tncs.c @@ -140,7 +140,7 @@ static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID) /* TNCS functions that IMVs can call */ -TNC_Result TNC_TNCS_ReportMessageTypes( +static TNC_Result TNC_TNCS_ReportMessageTypes( TNC_IMVID imvID, TNC_MessageTypeList supportedTypes, TNC_UInt32 typeCount) @@ -173,7 +173,7 @@ TNC_Result TNC_TNCS_ReportMessageTypes( } -TNC_Result TNC_TNCS_SendMessage( +static TNC_Result TNC_TNCS_SendMessage( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_BufferReference message, @@ -222,7 +222,7 @@ TNC_Result TNC_TNCS_SendMessage( } -TNC_Result TNC_TNCS_RequestHandshakeRetry( +static TNC_Result TNC_TNCS_RequestHandshakeRetry( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_RetryReason reason) @@ -233,7 +233,7 @@ TNC_Result TNC_TNCS_RequestHandshakeRetry( } -TNC_Result TNC_TNCS_ProvideRecommendation( +static TNC_Result TNC_TNCS_ProvideRecommendation( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_IMV_Action_Recommendation recommendation, @@ -260,7 +260,7 @@ TNC_Result TNC_TNCS_ProvideRecommendation( } -TNC_Result TNC_TNCS_GetAttribute( +static TNC_Result TNC_TNCS_GetAttribute( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_AttributeID attribureID, @@ -274,7 +274,7 @@ TNC_Result TNC_TNCS_GetAttribute( } -TNC_Result TNC_TNCS_SetAttribute( +static TNC_Result TNC_TNCS_SetAttribute( TNC_IMVID imvID, TNC_ConnectionID connectionID, TNC_AttributeID attribureID, @@ -287,7 +287,7 @@ TNC_Result TNC_TNCS_SetAttribute( } -TNC_Result TNC_TNCS_BindFunction( +static TNC_Result TNC_TNCS_BindFunction( TNC_IMVID imvID, char *functionName, void **pOutFunctionPointer) diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c index ff33d286223fd..ff673bb2e7858 100644 --- a/src/eapol_auth/eapol_auth_sm.c +++ b/src/eapol_auth/eapol_auth_sm.c @@ -866,10 +866,13 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, sm->radius_cui = wpabuf_alloc_copy(radius_cui, os_strlen(radius_cui)); - sm->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo++; - if (eapol->acct_multi_session_id_lo == 0) - eapol->acct_multi_session_id_hi++; - sm->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi; +#ifndef CONFIG_NO_RADIUS + if (radius_gen_session_id((u8 *) &sm->acct_multi_session_id, + sizeof(sm->acct_multi_session_id)) < 0) { + eapol_auth_free(sm); + return NULL; + } +#endif /* CONFIG_NO_RADIUS */ return sm; } @@ -884,6 +887,9 @@ void eapol_auth_free(struct eapol_state_machine *sm) eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL); if (sm->eap) eap_server_sm_deinit(sm->eap); + + wpabuf_free(sm->radius_cui); + os_free(sm->identity); os_free(sm); } @@ -1271,7 +1277,6 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, struct eapol_auth_cb *cb) { struct eapol_authenticator *eapol; - struct os_time now; eapol = os_zalloc(sizeof(*eapol)); if (eapol == NULL) @@ -1300,12 +1305,6 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, eapol->cb.erp_get_key = cb->erp_get_key; eapol->cb.erp_add_key = cb->erp_add_key; - /* Acct-Multi-Session-Id should be unique over reboots. If reliable - * clock is not available, this could be replaced with reboot counter, - * etc. */ - os_get_time(&now); - eapol->acct_multi_session_id_hi = now.sec; - return eapol; } diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h index a29b49c90c72f..04386b2ce5514 100644 --- a/src/eapol_auth/eapol_auth_sm_i.h +++ b/src/eapol_auth/eapol_auth_sm_i.h @@ -30,9 +30,6 @@ struct eapol_authenticator { u8 *default_wep_key; u8 default_wep_key_idx; - - u32 acct_multi_session_id_hi; - u32 acct_multi_session_id_lo; }; @@ -162,12 +159,6 @@ struct eapol_state_machine { struct radius_class_data radius_class; struct wpabuf *radius_cui; /* Chargeable-User-Identity */ - /* Keys for encrypting and signing EAPOL-Key frames */ - u8 *eapol_key_sign; - size_t eapol_key_sign_len; - u8 *eapol_key_crypt; - size_t eapol_key_crypt_len; - struct eap_sm *eap; Boolean initializing; /* in process of initializing state machines */ @@ -179,8 +170,7 @@ struct eapol_state_machine { int remediation; - u32 acct_multi_session_id_hi; - u32 acct_multi_session_id_lo; + u64 acct_multi_session_id; }; #endif /* EAPOL_AUTH_SM_I_H */ diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index 09cf4f6b92225..65460fc3bec05 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -314,6 +314,16 @@ SM_STATE(SUPP_PAE, RESTART) { SM_ENTRY(SUPP_PAE, RESTART); sm->eapRestart = TRUE; + if (sm->altAccept) { + /* + * Prevent EAP peer state machine from failing due to prior + * external EAP success notification (altSuccess=TRUE in the + * IDLE state could result in a transition to the FAILURE state. + */ + wpa_printf(MSG_DEBUG, "EAPOL: Clearing prior altAccept TRUE"); + sm->eapSuccess = FALSE; + sm->altAccept = FALSE; + } } diff --git a/src/fst/fst.c b/src/fst/fst.c index 2880870213e61..32cd941b41e24 100644 --- a/src/fst/fst.c +++ b/src/fst/fst.c @@ -15,6 +15,7 @@ #include "fst/fst_defs.h" #include "fst/fst_ctrl_iface.h" +static int fst_global_initialized = 0; struct dl_list fst_global_ctrls_list; @@ -106,6 +107,7 @@ int fst_global_init(void) dl_list_init(&fst_global_groups_list); dl_list_init(&fst_global_ctrls_list); fst_session_global_init(); + fst_global_initialized = 1; return 0; } @@ -115,6 +117,9 @@ void fst_global_deinit(void) struct fst_group *group; struct fst_ctrl_handle *h; + if (!fst_global_initialized) + return; + fst_session_global_deinit(); while ((group = fst_first_group()) != NULL) fst_group_delete(group); @@ -122,6 +127,7 @@ void fst_global_deinit(void) struct fst_ctrl_handle, global_ctrls_lentry))) fst_global_del_ctrl(h); + fst_global_initialized = 0; } @@ -160,7 +166,7 @@ void fst_global_del_ctrl(struct fst_ctrl_handle *h) void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt, size_t len) { - if (fst_iface_is_connected(iface, mgmt->sa)) + if (fst_iface_is_connected(iface, mgmt->sa, FALSE)) fst_session_on_action_rx(iface, mgmt, len); else wpa_printf(MSG_DEBUG, diff --git a/src/fst/fst_ctrl_aux.c b/src/fst/fst_ctrl_aux.c index dc7b2a7d72021..b6328279b745e 100644 --- a/src/fst/fst_ctrl_aux.c +++ b/src/fst/fst_ctrl_aux.c @@ -14,27 +14,28 @@ static const char *session_event_names[] = { - [EVENT_FST_ESTABLISHED] FST_PVAL_EVT_TYPE_ESTABLISHED, - [EVENT_FST_SETUP] FST_PVAL_EVT_TYPE_SETUP, - [EVENT_FST_SESSION_STATE_CHANGED] FST_PVAL_EVT_TYPE_SESSION_STATE, + [EVENT_FST_ESTABLISHED] = FST_PVAL_EVT_TYPE_ESTABLISHED, + [EVENT_FST_SETUP] = FST_PVAL_EVT_TYPE_SETUP, + [EVENT_FST_SESSION_STATE_CHANGED] = FST_PVAL_EVT_TYPE_SESSION_STATE, }; static const char *reason_names[] = { - [REASON_TEARDOWN] FST_CS_PVAL_REASON_TEARDOWN, - [REASON_SETUP] FST_CS_PVAL_REASON_SETUP, - [REASON_SWITCH] FST_CS_PVAL_REASON_SWITCH, - [REASON_STT] FST_CS_PVAL_REASON_STT, - [REASON_REJECT] FST_CS_PVAL_REASON_REJECT, - [REASON_ERROR_PARAMS] FST_CS_PVAL_REASON_ERROR_PARAMS, - [REASON_RESET] FST_CS_PVAL_REASON_RESET, - [REASON_DETACH_IFACE] FST_CS_PVAL_REASON_DETACH_IFACE, + [REASON_TEARDOWN] = FST_CS_PVAL_REASON_TEARDOWN, + [REASON_SETUP] = FST_CS_PVAL_REASON_SETUP, + [REASON_SWITCH] = FST_CS_PVAL_REASON_SWITCH, + [REASON_STT] = FST_CS_PVAL_REASON_STT, + [REASON_REJECT] = FST_CS_PVAL_REASON_REJECT, + [REASON_ERROR_PARAMS] = FST_CS_PVAL_REASON_ERROR_PARAMS, + [REASON_RESET] = FST_CS_PVAL_REASON_RESET, + [REASON_DETACH_IFACE] = FST_CS_PVAL_REASON_DETACH_IFACE, }; static const char *session_state_names[] = { - [FST_SESSION_STATE_INITIAL] FST_CS_PVAL_STATE_INITIAL, - [FST_SESSION_STATE_SETUP_COMPLETION] FST_CS_PVAL_STATE_SETUP_COMPLETION, - [FST_SESSION_STATE_TRANSITION_DONE] FST_CS_PVAL_STATE_TRANSITION_DONE, - [FST_SESSION_STATE_TRANSITION_CONFIRMED] + [FST_SESSION_STATE_INITIAL] = FST_CS_PVAL_STATE_INITIAL, + [FST_SESSION_STATE_SETUP_COMPLETION] = + FST_CS_PVAL_STATE_SETUP_COMPLETION, + [FST_SESSION_STATE_TRANSITION_DONE] = FST_CS_PVAL_STATE_TRANSITION_DONE, + [FST_SESSION_STATE_TRANSITION_CONFIRMED] = FST_CS_PVAL_STATE_TRANSITION_CONFIRMED, }; diff --git a/src/fst/fst_ctrl_iface.c b/src/fst/fst_ctrl_iface.c index d0907188a389e..7820e586629f2 100644 --- a/src/fst/fst_ctrl_iface.c +++ b/src/fst/fst_ctrl_iface.c @@ -648,9 +648,9 @@ static int list_groups(const char *cmd, char *buf, size_t buflen) static const char * band_freq(enum mb_band_id band) { static const char *band_names[] = { - [MB_BAND_ID_WIFI_2_4GHZ] "2.4GHZ", - [MB_BAND_ID_WIFI_5GHZ] "5GHZ", - [MB_BAND_ID_WIFI_60GHZ] "60GHZ", + [MB_BAND_ID_WIFI_2_4GHZ] = "2.4GHZ", + [MB_BAND_ID_WIFI_5GHZ] = "5GHZ", + [MB_BAND_ID_WIFI_60GHZ] = "60GHZ", }; return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names)); @@ -749,7 +749,7 @@ int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen) foreach_fst_group(g) { foreach_fst_group_iface(g, f) { - if (fst_iface_is_connected(f, addr)) { + if (fst_iface_is_connected(f, addr, TRUE)) { ret += print_band(num++, f, addr, buf + ret, buflen - ret); } diff --git a/src/fst/fst_defs.h b/src/fst/fst_defs.h index 8ddcc61376b2d..5859f6f5ed918 100644 --- a/src/fst/fst_defs.h +++ b/src/fst/fst_defs.h @@ -34,7 +34,7 @@ enum session_type { struct session_transition_ie { u8 element_id; u8 length; - u32 fsts_id; + le32 fsts_id; u8 session_control; u8 new_band_id; u8 new_band_setup; @@ -47,7 +47,7 @@ struct session_transition_ie { struct fst_setup_req { u8 action; u8 dialog_token; - u32 llt; + le32 llt; struct session_transition_ie stie; /* Multi-band (optional) */ /* Wakeup Schedule (optional) */ @@ -70,18 +70,18 @@ struct fst_setup_res { struct fst_ack_req { u8 action; u8 dialog_token; - u32 fsts_id; + le32 fsts_id; } STRUCT_PACKED; struct fst_ack_res { u8 action; u8 dialog_token; - u32 fsts_id; + le32 fsts_id; } STRUCT_PACKED; struct fst_tear_down { u8 action; - u32 fsts_id; + le32 fsts_id; } STRUCT_PACKED; #endif /* IEEE_80211_FST_DEFS_H */ diff --git a/src/fst/fst_group.c b/src/fst/fst_group.c index f6c7be9435f41..321d40d50cd23 100644 --- a/src/fst/fst_group.c +++ b/src/fst/fst_group.c @@ -18,22 +18,6 @@ struct dl_list fst_global_groups_list; -#ifndef HOSTAPD -static Boolean fst_has_fst_peer(struct fst_iface *iface, Boolean *has_peer) -{ - const u8 *bssid; - - bssid = fst_iface_get_bssid(iface); - if (!bssid) { - *has_peer = FALSE; - return FALSE; - } - - *has_peer = TRUE; - return fst_iface_get_peer_mb_ie(iface, bssid) != NULL; -} -#endif /* HOSTAPD */ - static void fst_dump_mb_ies(const char *group_id, const char *ifname, struct wpabuf *mbies) @@ -147,16 +131,6 @@ static struct wpabuf * fst_group_create_mb_ie(struct fst_group *g, struct fst_iface *f; unsigned int nof_mbies = 0; unsigned int nof_ifaces_added = 0; -#ifndef HOSTAPD - Boolean has_peer; - Boolean has_fst_peer; - - foreach_fst_group_iface(g, f) { - has_fst_peer = fst_has_fst_peer(f, &has_peer); - if (has_peer && !has_fst_peer) - return NULL; - } -#endif /* HOSTAPD */ foreach_fst_group_iface(g, f) { if (f == i) @@ -222,43 +196,35 @@ static const u8 * fst_mbie_get_peer_addr(const struct multi_band_ie *mbie) } -static struct fst_iface * -fst_group_get_new_iface_by_mbie_and_band_id(struct fst_group *g, - const u8 *mb_ies_buff, - size_t mb_ies_size, - u8 band_id, - u8 *iface_peer_addr) +static const u8 * fst_mbie_get_peer_addr_for_band(const struct wpabuf *mbies, + u8 band_id) { - while (mb_ies_size >= 2) { - const struct multi_band_ie *mbie = - (const struct multi_band_ie *) mb_ies_buff; - - if (mbie->eid != WLAN_EID_MULTI_BAND || - (size_t) 2 + mbie->len < sizeof(*mbie)) - break; + const u8 *p = wpabuf_head(mbies); + size_t s = wpabuf_len(mbies); - if (mbie->band_id == band_id) { - struct fst_iface *iface; + while (s >= 2) { + const struct multi_band_ie *mbie = + (const struct multi_band_ie *) p; - foreach_fst_group_iface(g, iface) { - const u8 *peer_addr = - fst_mbie_get_peer_addr(mbie); + if (mbie->eid != WLAN_EID_MULTI_BAND) { + fst_printf(MSG_INFO, "unexpected eid %d", mbie->eid); + return NULL; + } - if (peer_addr && - fst_iface_is_connected(iface, peer_addr) && - band_id == fst_iface_get_band_id(iface)) { - os_memcpy(iface_peer_addr, peer_addr, - ETH_ALEN); - return iface; - } - } - break; + if (mbie->len < sizeof(*mbie) - 2 || mbie->len > s - 2) { + fst_printf(MSG_INFO, "invalid mbie len %d", + mbie->len); + return NULL; } - mb_ies_buff += 2 + mbie->len; - mb_ies_size -= 2 + mbie->len; + if (mbie->band_id == band_id) + return fst_mbie_get_peer_addr(mbie); + + p += 2 + mbie->len; + s -= 2 + mbie->len; } + fst_printf(MSG_INFO, "mbie doesn't contain band %d", band_id); return NULL; } @@ -295,78 +261,172 @@ u32 fst_group_assign_fsts_id(struct fst_group *g) } -static Boolean -fst_group_does_iface_appear_in_other_mbies(struct fst_group *g, - struct fst_iface *iface, - struct fst_iface *other, - u8 *peer_addr) +/** + * fst_group_get_peer_other_connection_1 - Find peer's "other" connection + * (iface, MAC tuple) by using peer's MB IE on iface. + * + * @iface: iface on which FST Setup Request was received + * @peer_addr: Peer address on iface + * @band_id: "other" connection band id + * @other_peer_addr (out): Peer's MAC address on the "other" connection (on the + * "other" iface) + * + * This function parses peer's MB IE on iface. It looks for peer's MAC address + * on band_id (tmp_peer_addr). Next all interfaces are iterated to find an + * interface which correlates with band_id. If such interface is found, peer + * database is iterated to see if tmp_peer_addr is connected over it. + */ +static struct fst_iface * +fst_group_get_peer_other_connection_1(struct fst_iface *iface, + const u8 *peer_addr, u8 band_id, + u8 *other_peer_addr) { - struct fst_get_peer_ctx *ctx; - const u8 *addr; - const u8 *iface_addr; - enum mb_band_id iface_band_id; + const struct wpabuf *mbies; + struct fst_iface *other_iface; + const u8 *tmp_peer_addr; - WPA_ASSERT(g == fst_iface_get_group(iface)); - WPA_ASSERT(g == fst_iface_get_group(other)); + /* Get peer's MB IEs on iface */ + mbies = fst_iface_get_peer_mb_ie(iface, peer_addr); + if (!mbies) + return NULL; - iface_addr = fst_iface_get_addr(iface); - iface_band_id = fst_iface_get_band_id(iface); + /* Get peer's MAC address on the "other" interface */ + tmp_peer_addr = fst_mbie_get_peer_addr_for_band(mbies, band_id); + if (!tmp_peer_addr) { + fst_printf(MSG_INFO, + "couldn't extract other peer addr from mbies"); + return NULL; + } - addr = fst_iface_get_peer_first(other, &ctx, TRUE); - for (; addr; addr = fst_iface_get_peer_next(other, &ctx, TRUE)) { - const struct wpabuf *mbies; - u8 other_iface_peer_addr[ETH_ALEN]; - struct fst_iface *other_new_iface; + fst_printf(MSG_DEBUG, "found other peer addr from mbies: " MACSTR, + MAC2STR(tmp_peer_addr)); - mbies = fst_iface_get_peer_mb_ie(other, addr); - if (!mbies) + foreach_fst_group_iface(fst_iface_get_group(iface), other_iface) { + if (other_iface == iface || + band_id != fst_iface_get_band_id(other_iface)) continue; - - other_new_iface = fst_group_get_new_iface_by_mbie_and_band_id( - g, wpabuf_head(mbies), wpabuf_len(mbies), - iface_band_id, other_iface_peer_addr); - if (other_new_iface == iface && - os_memcmp(iface_addr, other_iface_peer_addr, - ETH_ALEN) != 0) { - os_memcpy(peer_addr, addr, ETH_ALEN); - return TRUE; + if (fst_iface_is_connected(other_iface, tmp_peer_addr, FALSE)) { + os_memcpy(other_peer_addr, tmp_peer_addr, ETH_ALEN); + return other_iface; } } - return FALSE; + return NULL; } -struct fst_iface * -fst_group_find_new_iface_by_stie(struct fst_group *g, - struct fst_iface *iface, - const u8 *peer_addr, - const struct session_transition_ie *stie, - u8 *iface_peer_addr) +/** + * fst_group_get_peer_other_connection_2 - Find peer's "other" connection + * (iface, MAC tuple) by using MB IEs of other peers. + * + * @iface: iface on which FST Setup Request was received + * @peer_addr: Peer address on iface + * @band_id: "other" connection band id + * @other_peer_addr (out): Peer's MAC address on the "other" connection (on the + * "other" iface) + * + * This function iterates all connection (other_iface, cur_peer_addr tuples). + * For each connection, MB IE (of cur_peer_addr on other_iface) is parsed and + * MAC address on iface's band_id is extracted (this_peer_addr). + * this_peer_addr is then compared to peer_addr. A match indicates we have + * found the "other" connection. + */ +static struct fst_iface * +fst_group_get_peer_other_connection_2(struct fst_iface *iface, + const u8 *peer_addr, u8 band_id, + u8 *other_peer_addr) { - struct fst_iface *i; + u8 this_band_id = fst_iface_get_band_id(iface); + const u8 *cur_peer_addr, *this_peer_addr; + struct fst_get_peer_ctx *ctx; + struct fst_iface *other_iface; + const struct wpabuf *cur_mbie; - foreach_fst_group_iface(g, i) { - if (i == iface || - stie->new_band_id != fst_iface_get_band_id(i)) + foreach_fst_group_iface(fst_iface_get_group(iface), other_iface) { + if (other_iface == iface || + band_id != fst_iface_get_band_id(other_iface)) continue; - if (fst_group_does_iface_appear_in_other_mbies(g, iface, i, - iface_peer_addr)) - return i; - break; + cur_peer_addr = fst_iface_get_peer_first(other_iface, &ctx, + TRUE); + for (; cur_peer_addr; + cur_peer_addr = fst_iface_get_peer_next(other_iface, &ctx, + TRUE)) { + cur_mbie = fst_iface_get_peer_mb_ie(other_iface, + cur_peer_addr); + if (!cur_mbie) + continue; + this_peer_addr = fst_mbie_get_peer_addr_for_band( + cur_mbie, this_band_id); + if (!this_peer_addr) + continue; + if (os_memcmp(this_peer_addr, peer_addr, ETH_ALEN) == + 0) { + os_memcpy(other_peer_addr, cur_peer_addr, + ETH_ALEN); + return other_iface; + } + } } + return NULL; } +/** + * fst_group_get_peer_other_connection - Find peer's "other" connection (iface, + * MAC tuple). + * + * @iface: iface on which FST Setup Request was received + * @peer_addr: Peer address on iface + * @band_id: "other" connection band id + * @other_peer_addr (out): Peer's MAC address on the "other" connection (on the + * "other" iface) + * + * This function is called upon receiving FST Setup Request from some peer who + * has peer_addr on iface. It searches for another connection of the same peer + * on different interface which correlates with band_id. MB IEs received from + * peer (on the two different interfaces) are used to identify same peer. + */ struct fst_iface * -fst_group_get_new_iface_by_stie_and_mbie( - struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size, - const struct session_transition_ie *stie, u8 *iface_peer_addr) +fst_group_get_peer_other_connection(struct fst_iface *iface, + const u8 *peer_addr, u8 band_id, + u8 *other_peer_addr) { - return fst_group_get_new_iface_by_mbie_and_band_id( - g, mb_ies_buff, mb_ies_size, stie->new_band_id, - iface_peer_addr); + struct fst_iface *other_iface; + + fst_printf(MSG_DEBUG, "%s: %s:" MACSTR ", %d", __func__, + fst_iface_get_name(iface), MAC2STR(peer_addr), band_id); + + /* + * Two search methods are used: + * 1. Use peer's MB IE on iface to extract peer's MAC address on + * "other" connection. Then check if such "other" connection exists. + * 2. Iterate peer database, examine each MB IE to see if it points to + * (iface, peer_addr) tuple + */ + + other_iface = fst_group_get_peer_other_connection_1(iface, peer_addr, + band_id, + other_peer_addr); + if (other_iface) { + fst_printf(MSG_DEBUG, "found by method #1. %s:" MACSTR, + fst_iface_get_name(other_iface), + MAC2STR(other_peer_addr)); + return other_iface; + } + + other_iface = fst_group_get_peer_other_connection_2(iface, peer_addr, + band_id, + other_peer_addr); + if (other_iface) { + fst_printf(MSG_DEBUG, "found by method #2. %s:" MACSTR, + fst_iface_get_name(other_iface), + MAC2STR(other_peer_addr)); + return other_iface; + } + + fst_printf(MSG_INFO, "%s: other connection not found", __func__); + return NULL; } diff --git a/src/fst/fst_group.h b/src/fst/fst_group.h index 3a87c0bc91c91..00aee9c8c25c9 100644 --- a/src/fst/fst_group.h +++ b/src/fst/fst_group.h @@ -48,15 +48,9 @@ Boolean fst_group_delete_if_empty(struct fst_group *group); struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g, const char *ifname); struct fst_iface * -fst_group_find_new_iface_by_stie(struct fst_group *g, - struct fst_iface *iface, - const u8 *peer_addr, - const struct session_transition_ie *stie, - u8 *iface_peer_addr); -struct fst_iface * -fst_group_get_new_iface_by_stie_and_mbie( - struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size, - const struct session_transition_ie *stie, u8 *iface_peer_addr); +fst_group_get_peer_other_connection(struct fst_iface *iface, + const u8 *peer_addr, u8 band_id, + u8 *other_peer_addr); u8 fst_group_assign_dialog_token(struct fst_group *g); u32 fst_group_assign_fsts_id(struct fst_group *g); diff --git a/src/fst/fst_iface.c b/src/fst/fst_iface.c index 5a92d2c33e420..35e83cb7b4710 100644 --- a/src/fst/fst_iface.c +++ b/src/fst/fst_iface.c @@ -49,12 +49,13 @@ void fst_iface_delete(struct fst_iface *i) } -Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr) +Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr, + Boolean mb_only) { struct fst_get_peer_ctx *ctx; - const u8 *a = fst_iface_get_peer_first(iface, &ctx, TRUE); + const u8 *a = fst_iface_get_peer_first(iface, &ctx, mb_only); - for (; a != NULL; a = fst_iface_get_peer_next(iface, &ctx, TRUE)) + for (; a != NULL; a = fst_iface_get_peer_next(iface, &ctx, mb_only)) if (os_memcmp(addr, a, ETH_ALEN) == 0) return TRUE; diff --git a/src/fst/fst_iface.h b/src/fst/fst_iface.h index 4670d894f7cbd..0eb27325a2b88 100644 --- a/src/fst/fst_iface.h +++ b/src/fst/fst_iface.h @@ -123,7 +123,8 @@ static inline const u8 * fst_iface_get_peer_next(struct fst_iface *i, return i->iface_obj.get_peer_next(i->iface_obj.ctx, ctx, mb_only); } -Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr); +Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr, + Boolean mb_only); void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie); enum mb_band_id fst_iface_get_band_id(struct fst_iface *i); diff --git a/src/fst/fst_session.c b/src/fst/fst_session.c index 55fa69495e99a..76e2c78f4ff6c 100644 --- a/src/fst/fst_session.c +++ b/src/fst/fst_session.c @@ -44,7 +44,7 @@ #define FST_LLT_MS_DEFAULT 50 #define FST_ACTION_MAX_SUPPORTED FST_ACTION_ON_CHANNEL_TUNNEL -const char * const fst_action_names[] = { +static const char * const fst_action_names[] = { [FST_ACTION_SETUP_REQUEST] = "Setup Request", [FST_ACTION_SETUP_RESPONSE] = "Setup Response", [FST_ACTION_TEAR_DOWN] = "Tear Down", @@ -181,7 +181,8 @@ static void fst_session_timeout_handler(void *eloop_data, void *user_ctx) static void fst_session_stt_arm(struct fst_session *s) { - eloop_register_timeout(0, TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU), + /* Action frames sometimes get delayed. Use relaxed timeout (2*) */ + eloop_register_timeout(0, 2 * TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU), fst_session_timeout_handler, NULL, s); s->stt_armed = TRUE; } @@ -363,7 +364,6 @@ static void fst_session_handle_setup_request(struct fst_iface *iface, struct fst_iface *new_iface = NULL; struct fst_group *g; u8 new_iface_peer_addr[ETH_ALEN]; - const struct wpabuf *peer_mbies; size_t plen; if (frame_len < IEEE80211_HDRLEN + 1 + sizeof(*req)) { @@ -399,36 +399,18 @@ static void fst_session_handle_setup_request(struct fst_iface *iface, MAC2STR(mgmt->sa)); } - peer_mbies = fst_iface_get_peer_mb_ie(iface, mgmt->sa); - if (peer_mbies) { - new_iface = fst_group_get_new_iface_by_stie_and_mbie( - g, wpabuf_head(peer_mbies), wpabuf_len(peer_mbies), - &req->stie, new_iface_peer_addr); - if (new_iface) - fst_printf_iface(iface, MSG_INFO, - "FST Request: new iface (%s:" MACSTR - ") found by MB IEs", - fst_iface_get_name(new_iface), - MAC2STR(new_iface_peer_addr)); - } - - if (!new_iface) { - new_iface = fst_group_find_new_iface_by_stie( - g, iface, mgmt->sa, &req->stie, - new_iface_peer_addr); - if (new_iface) - fst_printf_iface(iface, MSG_INFO, - "FST Request: new iface (%s:" MACSTR - ") found by others", - fst_iface_get_name(new_iface), - MAC2STR(new_iface_peer_addr)); - } - + new_iface = fst_group_get_peer_other_connection(iface, mgmt->sa, + req->stie.new_band_id, + new_iface_peer_addr); if (!new_iface) { fst_printf_iface(iface, MSG_WARNING, "FST Request dropped: new iface not found"); return; } + fst_printf_iface(iface, MSG_INFO, + "FST Request: new iface (%s:" MACSTR ") found", + fst_iface_get_name(new_iface), + MAC2STR(new_iface_peer_addr)); s = fst_find_session_in_progress(mgmt->sa, g); if (s) { @@ -447,7 +429,9 @@ static void fst_session_handle_setup_request(struct fst_iface *iface, * the initiator’s MAC address, in which case, the responder * shall delete the received FST Setup Request. */ - if (os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) { + if (fst_session_is_ready_pending(s) && + /* waiting for Setup Response */ + os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) { fst_printf_session(s, MSG_WARNING, "FST Request dropped due to MAC comparison (our MAC is " MACSTR ")", @@ -455,23 +439,26 @@ static void fst_session_handle_setup_request(struct fst_iface *iface, return; } - if (!fst_session_is_ready_pending(s)) { - fst_printf_session(s, MSG_WARNING, - "FST Request from " MACSTR - " dropped due to inappropriate state %s", - MAC2STR(mgmt->da), - fst_session_state_name(s->state)); - return; - } + /* + * State is SETUP_COMPLETION (either in transition or not) or + * TRANSITION_DONE (in transition). + * Setup Request arriving in this state could mean: + * 1. peer sent it before receiving our Setup Request (race + * condition) + * 2. peer didn't receive our Setup Response. Peer is retrying + * after STT timeout + * 3. peer's FST state machines are out of sync due to some + * other reason + * + * We will reset our session and create a new one instead. + */ + fst_printf_session(s, MSG_WARNING, + "resetting due to FST request"); /* * If FST Setup Request arrived with the same FSTS ID as one we - * initialized before, it means the other side either didn't - * receive our FST Request or skipped it for some reason (for - * example, due to numerical MAC comparison). - * - * In this case, there's no need to tear down the session. + * initialized before, there's no need to tear down the session. * Moreover, as FSTS ID is the same, the other side will * associate this tear down with the session it initiated that * will break the sync. @@ -483,7 +470,6 @@ static void fst_session_handle_setup_request(struct fst_iface *iface, "Skipping TearDown as the FST request has the same FSTS ID as initiated"); fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext); fst_session_stt_disarm(s); - fst_printf_session(s, MSG_WARNING, "reset due to FST request"); } s = fst_session_create(g); @@ -521,7 +507,9 @@ static void fst_session_handle_setup_response(struct fst_session *s, enum hostapd_hw_mode hw_mode; u8 channel; union fst_session_state_switch_extra evext = { - .to_initial = {0}, + .to_initial = { + .reject_code = 0, + }, }; if (iface != s->data.old_iface) { @@ -863,13 +851,15 @@ int fst_session_initiate_setup(struct fst_session *s) return -EINVAL; } - if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) { + if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr, + FALSE)) { fst_printf_session(s, MSG_ERROR, "The preset old peer address is not connected"); return -EINVAL; } - if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr)) { + if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr, + FALSE)) { fst_printf_session(s, MSG_ERROR, "The preset new peer address is not connected"); return -EINVAL; @@ -966,7 +956,8 @@ int fst_session_respond(struct fst_session *s, u8 status_code) return -EINVAL; } - if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) { + if (!fst_iface_is_connected(s->data.old_iface, + s->data.old_peer_addr, FALSE)) { fst_printf_session(s, MSG_ERROR, "The preset peer address is not in the peer list"); return -EINVAL; @@ -984,7 +975,7 @@ int fst_session_respond(struct fst_session *s, u8 status_code) res.stie.length = sizeof(res.stie) - 2; if (status_code == WLAN_STATUS_SUCCESS) { - res.stie.fsts_id = s->data.fsts_id; + res.stie.fsts_id = host_to_le32(s->data.fsts_id); res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); fst_iface_get_channel_info(s->data.new_iface, &hw_mode, @@ -1458,7 +1449,7 @@ int fst_test_req_send_fst_response(const char *params) res.stie.length = sizeof(res.stie) - 2; if (res.status_code == WLAN_STATUS_SUCCESS) { - res.stie.fsts_id = fsts_id; + res.stie.fsts_id = host_to_le32(fsts_id); res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0); fst_iface_get_channel_info(s.data.new_iface, &hw_mode, @@ -1507,7 +1498,7 @@ int fst_test_req_send_ack_request(const char *params) os_memset(&req, 0, sizeof(req)); req.action = FST_ACTION_ACK_REQUEST; req.dialog_token = g->dialog_token; - req.fsts_id = fsts_id; + req.fsts_id = host_to_le32(fsts_id); return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL); } @@ -1535,7 +1526,7 @@ int fst_test_req_send_ack_response(const char *params) os_memset(&res, 0, sizeof(res)); res.action = FST_ACTION_ACK_RESPONSE; res.dialog_token = g->dialog_token; - res.fsts_id = fsts_id; + res.fsts_id = host_to_le32(fsts_id); return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL); } @@ -1562,7 +1553,7 @@ int fst_test_req_send_tear_down(const char *params) os_memset(&td, 0, sizeof(td)); td.action = FST_ACTION_TEAR_DOWN; - td.fsts_id = fsts_id; + td.fsts_id = host_to_le32(fsts_id); return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL); } diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c index 41de2f85d9cde..a7a300e568e6d 100644 --- a/src/l2_packet/l2_packet_linux.c +++ b/src/l2_packet/l2_packet_linux.c @@ -30,11 +30,14 @@ struct l2_packet_data { int l2_hdr; /* whether to include layer 2 (Ethernet) header data * buffers */ +#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR /* For working around Linux packet socket behavior and regression. */ int fd_br_rx; - int last_from_br; + int last_from_br, last_from_br_prev; u8 last_hash[SHA1_MAC_LEN]; - unsigned int num_rx, num_rx_br; + u8 last_hash_prev[SHA1_MAC_LEN]; + unsigned int num_rx_br; +#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */ }; /* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and @@ -127,7 +130,6 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) struct sockaddr_ll ll; socklen_t fromlen; - l2->num_rx++; os_memset(&ll, 0, sizeof(ll)); fromlen = sizeof(ll); res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll, @@ -141,6 +143,7 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d", __func__, MAC2STR(ll.sll_addr), (int) res); +#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR if (l2->fd_br_rx >= 0) { u8 hash[SHA1_MAC_LEN]; const u8 *addr[1]; @@ -169,14 +172,24 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) __func__); return; } + if (l2->last_from_br_prev && + os_memcmp(hash, l2->last_hash_prev, SHA1_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX(prev)", + __func__); + return; + } + os_memcpy(l2->last_hash_prev, l2->last_hash, SHA1_MAC_LEN); + l2->last_from_br_prev = l2->last_from_br; os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN); } l2->last_from_br = 0; +#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */ l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res); } +#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx) { struct l2_packet_data *l2 = eloop_ctx; @@ -202,6 +215,11 @@ static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx) wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d", __func__, MAC2STR(ll.sll_addr), (int) res); + if (os_memcmp(ll.sll_addr, l2->own_addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "%s: Drop RX of own frame", __func__); + return; + } + addr[0] = buf; len[0] = res; sha1_vector(1, addr, len, hash); @@ -210,10 +228,18 @@ static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx) wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX", __func__); return; } + if (!l2->last_from_br_prev && + os_memcmp(hash, l2->last_hash_prev, SHA1_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX(prev)", __func__); + return; + } + os_memcpy(l2->last_hash_prev, l2->last_hash, SHA1_MAC_LEN); + l2->last_from_br_prev = l2->last_from_br; l2->last_from_br = 1; os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN); l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res); } +#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */ struct l2_packet_data * l2_packet_init( @@ -233,7 +259,9 @@ struct l2_packet_data * l2_packet_init( l2->rx_callback = rx_callback; l2->rx_callback_ctx = rx_callback_ctx; l2->l2_hdr = l2_hdr; +#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR l2->fd_br_rx = -1; +#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */ l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, htons(protocol)); @@ -289,6 +317,7 @@ struct l2_packet_data * l2_packet_init_bridge( void *rx_callback_ctx, int l2_hdr) { struct l2_packet_data *l2; +#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR struct sock_filter ethertype_sock_filter_insns[] = { /* Load ethertype */ BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 2 * ETH_ALEN), @@ -304,12 +333,14 @@ struct l2_packet_data * l2_packet_init_bridge( .filter = ethertype_sock_filter_insns, }; struct sockaddr_ll ll; +#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */ l2 = l2_packet_init(br_ifname, own_addr, protocol, rx_callback, rx_callback_ctx, l2_hdr); if (!l2) return NULL; +#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR /* * The Linux packet socket behavior has changed over the years and there * is an inconvenient regression in it that breaks RX for a specific @@ -357,6 +388,7 @@ struct l2_packet_data * l2_packet_init_bridge( } eloop_register_read_sock(l2->fd_br_rx, l2_packet_receive_br, l2, NULL); +#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */ return l2; } @@ -372,10 +404,12 @@ void l2_packet_deinit(struct l2_packet_data *l2) close(l2->fd); } +#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR if (l2->fd_br_rx >= 0) { eloop_unregister_read_sock(l2->fd_br_rx); close(l2->fd_br_rx); } +#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */ os_free(l2); } diff --git a/src/l2_packet/l2_packet_pcap.c b/src/l2_packet/l2_packet_pcap.c index bb4f4a31d0151..423c099fde88e 100644 --- a/src/l2_packet/l2_packet_pcap.c +++ b/src/l2_packet/l2_packet_pcap.c @@ -312,6 +312,18 @@ struct l2_packet_data * l2_packet_init( } +struct l2_packet_data * l2_packet_init_bridge( + const char *br_ifname, const char *ifname, const u8 *own_addr, + unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + return l2_packet_init(br_ifname, own_addr, protocol, rx_callback, + rx_callback_ctx, l2_hdr); +} + + void l2_packet_deinit(struct l2_packet_data *l2) { if (l2 == NULL) diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 767706c01d6b3..996b4e8249863 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -10,6 +10,7 @@ #include "common.h" #include "eloop.h" +#include "common/defs.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" @@ -445,8 +446,9 @@ static struct p2p_device * p2p_create_device(struct p2p_data *p2p, static void p2p_copy_client_info(struct p2p_device *dev, struct p2p_client_info *cli) { - os_memcpy(dev->info.device_name, cli->dev_name, cli->dev_name_len); - dev->info.device_name[cli->dev_name_len] = '\0'; + p2p_copy_filter_devname(dev->info.device_name, + sizeof(dev->info.device_name), + cli->dev_name, cli->dev_name_len); dev->info.dev_capab = cli->dev_capab; dev->info.config_methods = cli->config_methods; os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8); @@ -636,11 +638,11 @@ static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies, end = ies + ies_len; - for (pos = ies; pos + 1 < end; pos += len) { + for (pos = ies; end - pos > 1; pos += len) { id = *pos++; len = *pos++; - if (pos + len > end) + if (len > end - pos) break; if (id != WLAN_EID_VENDOR_SPECIFIC || len < 3) @@ -786,11 +788,11 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, dev->oper_ssid_len = msg.ssid[1]; } - if (msg.adv_service_instance && msg.adv_service_instance_len) { - wpabuf_free(dev->info.p2ps_instance); + wpabuf_free(dev->info.p2ps_instance); + dev->info.p2ps_instance = NULL; + if (msg.adv_service_instance && msg.adv_service_instance_len) dev->info.p2ps_instance = wpabuf_alloc_copy( msg.adv_service_instance, msg.adv_service_instance_len); - } if (freq >= 2412 && freq <= 2484 && msg.ds_params && *msg.ds_params >= 1 && *msg.ds_params <= 14) { @@ -1220,9 +1222,14 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; p2p_clear_timeout(p2p); + if (p2p->pending_listen_freq) { + p2p_dbg(p2p, "Clear pending_listen_freq for p2p_find"); + p2p->pending_listen_freq = 0; + } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); p2p->find_type = type; p2p_device_clear_reported(p2p); + os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN); p2p_set_state(p2p, P2P_SEARCH); p2p->search_delay = search_delay; p2p->in_search_delay = 0; @@ -1459,7 +1466,7 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p) /** - * p2p_prepare_channel - Select operating channel for GO Negotiation + * p2p_prepare_channel - Select operating channel for GO Negotiation or P2PS PD * @p2p: P2P module context from p2p_init() * @dev: Selected peer device * @force_freq: Forced frequency in MHz or 0 if not forced @@ -1468,9 +1475,9 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p) * Returns: 0 on success, -1 on failure (channel not supported for P2P) * * This function is used to do initial operating channel selection for GO - * Negotiation prior to having received peer information. The selected channel - * may be further optimized in p2p_reselect_channel() once the peer information - * is available. + * Negotiation prior to having received peer information or for P2PS PD + * signalling. The selected channel may be further optimized in + * p2p_reselect_channel() once the peer information is available. */ int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, unsigned int force_freq, unsigned int pref_freq, int go) @@ -2028,8 +2035,23 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, dev = p2p_get_device(p2p, addr); if (dev) { - if (dev->country[0] == 0 && msg.listen_channel) - os_memcpy(dev->country, msg.listen_channel, 3); + if (msg.listen_channel) { + int freq; + + if (dev->country[0] == 0) + os_memcpy(dev->country, msg.listen_channel, 3); + + freq = p2p_channel_to_freq(msg.listen_channel[3], + msg.listen_channel[4]); + + if (freq > 0 && dev->listen_freq != freq) { + p2p_dbg(p2p, + "Updated peer " MACSTR " Listen channel (Probe Request): %d -> %d MHz", + MAC2STR(addr), dev->listen_freq, freq); + dev->listen_freq = freq; + } + } + os_get_reltime(&dev->last_seen); p2p_parse_free(&msg); return; /* already known */ @@ -2212,6 +2234,58 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p, return buf; } +static int p2p_build_probe_resp_buf(struct p2p_data *p2p, struct wpabuf *buf, + struct wpabuf *ies, + const u8 *addr, int rx_freq) +{ + struct ieee80211_mgmt *resp; + u8 channel, op_class; + + resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, + u.probe_resp.variable)); + + resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_PROBE_RESP << 4)); + os_memcpy(resp->da, addr, ETH_ALEN); + os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN); + os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN); + resp->u.probe_resp.beacon_int = host_to_le16(100); + /* hardware or low-level driver will setup seq_ctrl and timestamp */ + resp->u.probe_resp.capab_info = + host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE | + WLAN_CAPABILITY_PRIVACY | + WLAN_CAPABILITY_SHORT_SLOT_TIME); + + wpabuf_put_u8(buf, WLAN_EID_SSID); + wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN); + wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); + + wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES); + wpabuf_put_u8(buf, 8); + wpabuf_put_u8(buf, (60 / 5) | 0x80); + wpabuf_put_u8(buf, 90 / 5); + wpabuf_put_u8(buf, (120 / 5) | 0x80); + wpabuf_put_u8(buf, 180 / 5); + wpabuf_put_u8(buf, (240 / 5) | 0x80); + wpabuf_put_u8(buf, 360 / 5); + wpabuf_put_u8(buf, 480 / 5); + wpabuf_put_u8(buf, 540 / 5); + + if (!rx_freq) { + channel = p2p->cfg->channel; + } else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) { + p2p_err(p2p, "Failed to convert freq to channel"); + return -1; + } + + wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS); + wpabuf_put_u8(buf, 1); + wpabuf_put_u8(buf, channel); + + wpabuf_put_buf(buf, ies); + + return 0; +} static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) { @@ -2245,10 +2319,8 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, { struct ieee802_11_elems elems; struct wpabuf *buf; - struct ieee80211_mgmt *resp; struct p2p_message msg; struct wpabuf *ies; - u8 channel, op_class; if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == ParseFailed) { @@ -2392,49 +2464,12 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, return P2P_PREQ_NOT_PROCESSED; } - resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, - u.probe_resp.variable)); - - resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | - (WLAN_FC_STYPE_PROBE_RESP << 4)); - os_memcpy(resp->da, addr, ETH_ALEN); - os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN); - os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN); - resp->u.probe_resp.beacon_int = host_to_le16(100); - /* hardware or low-level driver will setup seq_ctrl and timestamp */ - resp->u.probe_resp.capab_info = - host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE | - WLAN_CAPABILITY_PRIVACY | - WLAN_CAPABILITY_SHORT_SLOT_TIME); - - wpabuf_put_u8(buf, WLAN_EID_SSID); - wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN); - wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); - - wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES); - wpabuf_put_u8(buf, 8); - wpabuf_put_u8(buf, (60 / 5) | 0x80); - wpabuf_put_u8(buf, 90 / 5); - wpabuf_put_u8(buf, (120 / 5) | 0x80); - wpabuf_put_u8(buf, 180 / 5); - wpabuf_put_u8(buf, (240 / 5) | 0x80); - wpabuf_put_u8(buf, 360 / 5); - wpabuf_put_u8(buf, 480 / 5); - wpabuf_put_u8(buf, 540 / 5); - - if (!rx_freq) { - channel = p2p->cfg->channel; - } else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) { + if (p2p_build_probe_resp_buf(p2p, buf, ies, addr, rx_freq)) { wpabuf_free(ies); wpabuf_free(buf); return P2P_PREQ_NOT_PROCESSED; } - wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS); - wpabuf_put_u8(buf, 1); - wpabuf_put_u8(buf, channel); - - wpabuf_put_buf(buf, ies); wpabuf_free(ies); p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf, rx_freq); @@ -2448,12 +2483,18 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, - unsigned int rx_freq) + unsigned int rx_freq, int p2p_lo_started) { enum p2p_probe_req_status res; p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); + if (p2p_lo_started) { + p2p_dbg(p2p, + "Probe Response is offloaded, do not reply Probe Request"); + return P2P_PREQ_PROCESSED; + } + res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len, rx_freq); if (res != P2P_PREQ_PROCESSED && res != P2P_PREQ_NOT_PROCESSED) return res; @@ -2944,7 +2985,6 @@ void p2p_deinit(struct p2p_data *p2p) wpabuf_free(p2p->wfd_coupled_sink_info); #endif /* CONFIG_WIFI_DISPLAY */ - eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); @@ -2959,7 +2999,6 @@ void p2p_deinit(struct p2p_data *p2p) os_free(p2p->groups); p2ps_prov_free(p2p); wpabuf_free(p2p->sd_resp); - os_free(p2p->after_scan_tx); p2p_remove_wps_vendor_extensions(p2p); os_free(p2p->no_go_freq.range); p2p_service_flush_asp(p2p); @@ -2971,6 +3010,8 @@ void p2p_deinit(struct p2p_data *p2p) void p2p_flush(struct p2p_data *p2p) { struct p2p_device *dev, *prev; + + p2p_ext_listen(p2p, 0, 0); p2p_stop_find(p2p); dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device, list) { @@ -3157,13 +3198,18 @@ int p2p_set_country(struct p2p_data *p2p, const char *country) static int p2p_pre_find_operation(struct p2p_data *p2p, struct p2p_device *dev) { + int res; + if (dev->sd_pending_bcast_queries == 0) { /* Initialize with total number of registered broadcast * SD queries. */ dev->sd_pending_bcast_queries = p2p->num_p2p_sd_queries; } - if (p2p_start_sd(p2p, dev) == 0) + res = p2p_start_sd(p2p, dev); + if (res == -2) + return -2; + if (res == 0) return 1; if (dev->req_config_methods && @@ -3183,7 +3229,7 @@ static int p2p_pre_find_operation(struct p2p_data *p2p, struct p2p_device *dev) void p2p_continue_find(struct p2p_data *p2p) { struct p2p_device *dev; - int found; + int found, res; p2p_set_state(p2p, P2P_SEARCH); @@ -3196,10 +3242,13 @@ void p2p_continue_find(struct p2p_data *p2p) } if (!found) continue; - if (p2p_pre_find_operation(p2p, dev) > 0) { + res = p2p_pre_find_operation(p2p, dev); + if (res > 0) { p2p->last_p2p_find_oper = dev; return; } + if (res == -2) + goto skip_sd; } /* @@ -3207,14 +3256,19 @@ void p2p_continue_find(struct p2p_data *p2p) * iteration device. */ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { - if (p2p_pre_find_operation(p2p, dev) > 0) { + res = p2p_pre_find_operation(p2p, dev); + if (res > 0) { p2p->last_p2p_find_oper = dev; return; } + if (res == -2) + goto skip_sd; if (dev == p2p->last_p2p_find_oper) break; } +skip_sd: + os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN); p2p_listen_in_find(p2p, 1); } @@ -3226,8 +3280,17 @@ static void p2p_sd_cb(struct p2p_data *p2p, int success) p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (!success) { - if (p2p->sd_peer) + if (p2p->sd_peer) { + if (is_zero_ether_addr(p2p->sd_query_no_ack)) { + os_memcpy(p2p->sd_query_no_ack, + p2p->sd_peer->info.p2p_device_addr, + ETH_ALEN); + p2p_dbg(p2p, + "First SD Query no-ACK in this search iteration: " + MACSTR, MAC2STR(p2p->sd_query_no_ack)); + } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + } p2p->sd_peer = NULL; if (p2p->state != P2P_IDLE) p2p_continue_find(p2p); @@ -3326,6 +3389,43 @@ static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) } /* + * If after PD Request the peer doesn't expect to receive PD Response + * the PD Request ACK indicates a completion of the current PD. This + * happens only on the advertiser side sending the follow-on PD Request + * with the status different than 12 (Success: accepted by user). + */ + if (p2p->p2ps_prov && !p2p->p2ps_prov->pd_seeker && + p2p->p2ps_prov->status != P2P_SC_SUCCESS_DEFERRED) { + p2p_dbg(p2p, "P2PS PD completion on Follow-on PD Request ACK"); + + if (p2p->send_action_in_progress) { + p2p->send_action_in_progress = 0; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + } + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + if (p2p->cfg->p2ps_prov_complete) { + p2p->cfg->p2ps_prov_complete( + p2p->cfg->cb_ctx, + p2p->p2ps_prov->status, + p2p->p2ps_prov->adv_mac, + p2p->p2ps_prov->adv_mac, + p2p->p2ps_prov->session_mac, + NULL, p2p->p2ps_prov->adv_id, + p2p->p2ps_prov->session_id, + 0, 0, NULL, 0, 0, 0, + NULL, NULL, 0, 0, NULL, 0); + } + + if (p2p->user_initiated_pd) + p2p_reset_pending_pd(p2p); + + p2ps_prov_free(p2p); + return; + } + + /* * This postponing, of resetting pending_action_state, needs to be * done only for user initiated PD requests and not internal ones. */ @@ -3399,9 +3499,11 @@ int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, * operation was started. */ p2p_dbg(p2p, "Ignore old scan result for " MACSTR - " (rx_time=%u.%06u)", + " (rx_time=%u.%06u find_start=%u.%06u)", MAC2STR(bssid), (unsigned int) rx_time->sec, - (unsigned int) rx_time->usec); + (unsigned int) rx_time->usec, + (unsigned int) p2p->find_start.sec, + (unsigned int) p2p->find_start.usec); return 0; } @@ -3426,7 +3528,8 @@ void p2p_scan_res_handled(struct p2p_data *p2p) } -void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) +void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id, + unsigned int bands) { u8 dev_capab; u8 *len; @@ -3460,6 +3563,9 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period, p2p->ext_listen_interval); + if (bands & BAND_60_GHZ) + p2p_buf_add_device_info(ies, p2p, NULL); + if (p2p->p2ps_seek && p2p->p2ps_seek_count) p2p_buf_add_service_hash(ies, p2p); @@ -3694,6 +3800,8 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, break; case P2P_PENDING_INVITATION_RESPONSE: p2p_invitation_resp_cb(p2p, success); + if (p2p->inv_status != P2P_SC_SUCCESS) + p2p_check_after_scan_tx_continuation(p2p); break; case P2P_PENDING_DEV_DISC_REQUEST: p2p_dev_disc_req_cb(p2p, success); @@ -5400,3 +5508,34 @@ void p2p_set_own_pref_freq_list(struct p2p_data *p2p, i, p2p->pref_freq_list[i]); } } + + +struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p, + unsigned int freq) +{ + struct wpabuf *ies, *buf; + u8 addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + int ret; + + ies = p2p_build_probe_resp_ies(p2p, NULL, 0); + if (!ies) { + wpa_printf(MSG_ERROR, + "CTRL: Failed to build Probe Response IEs"); + return NULL; + } + + buf = wpabuf_alloc(200 + wpabuf_len(ies)); + if (!buf) { + wpabuf_free(ies); + return NULL; + } + + ret = p2p_build_probe_resp_buf(p2p, buf, ies, addr, freq); + wpabuf_free(ies); + if (ret) { + wpabuf_free(buf); + return NULL; + } + + return buf; +} diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index b4060be477b6b..7b18dcfc3ff3a 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -31,7 +31,7 @@ /** * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes */ -#define P2P_MAX_REG_CLASSES 10 +#define P2P_MAX_REG_CLASSES 15 /** * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class @@ -99,6 +99,10 @@ struct p2p_go_neg_results { int vht; + u8 max_oper_chwidth; + + unsigned int vht_center_freq2; + /** * ssid - SSID of the group */ @@ -224,6 +228,16 @@ struct p2ps_provision { u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; /** + * force_freq - The only allowed channel frequency in MHz or 0. + */ + unsigned int force_freq; + + /** + * pref_freq - Preferred operating frequency in MHz or 0. + */ + unsigned int pref_freq; + + /** * info - Vendor defined extra Provisioning information */ char info[0]; @@ -1024,6 +1038,8 @@ struct p2p_config { * @ssid_len: Buffer for returning length of @ssid * @group_iface: Buffer for returning whether a separate group interface * would be used + * @freq: Variable for returning the current operating frequency of a + * currently running P2P GO. * Returns: 1 if GO info found, 0 otherwise * * This is used to compose New Group settings (SSID, and intended @@ -1031,7 +1047,8 @@ struct p2p_config { * result in our being an autonomous GO. */ int (*get_go_info)(void *ctx, u8 *intended_addr, - u8 *ssid, size_t *ssid_len, int *group_iface); + u8 *ssid, size_t *ssid_len, int *group_iface, + unsigned int *freq); /** * remove_stale_groups - Remove stale P2PS groups @@ -1056,7 +1073,9 @@ struct p2p_config { const u8 *persist_ssid, size_t persist_ssid_size, int response_done, int prov_start, const char *session_info, - const u8 *feat_cap, size_t feat_cap_len); + const u8 *feat_cap, size_t feat_cap_len, + unsigned int freq, const u8 *group_ssid, + size_t group_ssid_len); /** * prov_disc_resp_cb - Callback for indicating completion of PD Response @@ -1070,14 +1089,20 @@ struct p2p_config { /** * p2ps_group_capability - Determine group capability + * @ctx: Callback context from cb_ctx + * @incoming: Peer requested roles, expressed with P2PS_SETUP_* bitmap. + * @role: Local roles, expressed with P2PS_SETUP_* bitmap. + * @force_freq: Variable for returning forced frequency for the group. + * @pref_freq: Variable for returning preferred frequency for the group. + * Returns: P2PS_SETUP_* bitmap of group capability result. * - * This function can be used to determine group capability based on - * information from P2PS PD exchange and the current state of ongoing - * groups and driver capabilities. - * - * P2PS_SETUP_* bitmap is used as the parameters and return value. + * This function can be used to determine group capability and + * frequencies based on information from P2PS PD exchange and the + * current state of ongoing groups and driver capabilities. */ - u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role); + u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role, + unsigned int *force_freq, + unsigned int *pref_freq); /** * get_pref_freq_list - Get preferred frequency list for an interface @@ -1530,12 +1555,13 @@ enum p2p_probe_req_status { * @ie: Information elements from the Probe Request frame body * @ie_len: Length of ie buffer in octets * @rx_freq: Probe Request frame RX frequency + * @p2p_lo_started: Whether P2P Listen Offload is started * Returns: value indicating the type and status of the probe request */ enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, - unsigned int rx_freq); + unsigned int rx_freq, int p2p_lo_started); /** * p2p_rx_action - Report received Action frame @@ -1691,6 +1717,12 @@ struct p2p_group_config { int freq; /** + * ip_addr_alloc - Whether IP address allocation within 4-way handshake + * is supported + */ + int ip_addr_alloc; + + /** * cb_ctx - Context to use with callback functions */ void *cb_ctx; @@ -1877,8 +1909,10 @@ int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, * @p2p: P2P module context from p2p_init() * @ies: Buffer for writing P2P IE * @dev_id: Device ID to search for or %NULL for any + * @bands: Frequency bands used in the scan (enum wpa_radio_work_band bitmap) */ -void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id); +void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id, + unsigned int bands); /** * p2p_scan_ie_buf_len - Get maximum buffer length needed for p2p_scan_ie @@ -2100,6 +2134,16 @@ int p2p_client_limit_reached(struct p2p_group *group); const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next); /** + * p2p_group_get_client_interface_addr - Get P2P Interface Address of a client in a group + * @group: P2P group context from p2p_group_init() + * @dev_addr: P2P Device Address of the client + * Returns: P2P Interface Address of the client if found or %NULL if no match + * found + */ +const u8 * p2p_group_get_client_interface_addr(struct p2p_group *group, + const u8 *dev_addr); + +/** * p2p_group_get_dev_addr - Get a P2P Device Address of a client in a group * @group: P2P group context from p2p_group_init() * @addr: P2P Interface Address of the client @@ -2241,7 +2285,7 @@ struct wpabuf * wifi_display_encaps(struct wpabuf *subelems); * discovery (p2p_find). A random number of 100 TU units is picked for each * Listen state iteration from [min_disc_int,max_disc_int] range. * - * max_disc_tu can be used to futher limit the discoverable duration. However, + * max_disc_tu can be used to further limit the discoverable duration. However, * it should be noted that use of this parameter is not recommended since it * would not be compliant with the P2P specification. */ @@ -2340,4 +2384,7 @@ void p2p_set_own_pref_freq_list(struct p2p_data *p2p, int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, unsigned int *num); +struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p, + unsigned int freq); + #endif /* P2P_H */ diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c index 793d28ba7bdd2..2882c6ad02e7e 100644 --- a/src/p2p/p2p_build.c +++ b/src/p2p/p2p_build.c @@ -202,11 +202,11 @@ void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p, if (peer && peer->wps_method != WPS_NOT_READY) { if (peer->wps_method == WPS_PBC) methods |= WPS_CONFIG_PUSHBUTTON; + else if (peer->wps_method == WPS_P2PS) + methods |= WPS_CONFIG_P2PS; else if (peer->wps_method == WPS_PIN_DISPLAY || - peer->wps_method == WPS_PIN_KEYPAD) { + peer->wps_method == WPS_PIN_KEYPAD) methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; - methods |= WPS_CONFIG_P2PS; - } } else if (p2p->cfg->config_methods) { methods |= p2p->cfg->config_methods & (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY | diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 83b43563d945f..9f0b3f3d37a44 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -38,7 +38,7 @@ int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, { const u8 *pos, *end; struct p2p_channels *ch; - size_t channels; + u8 channels; struct p2p_channels intersection; ch = &dev->channels; @@ -58,14 +58,14 @@ int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, } pos += 3; - while (pos + 2 < end) { + while (end - pos > 2) { struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes]; cl->reg_class = *pos++; - if (pos + 1 + pos[0] > end) { + channels = *pos++; + if (channels > end - pos) { p2p_info(p2p, "Invalid peer Channel List"); return -1; } - channels = *pos++; cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ? P2P_MAX_REG_CLASS_CHANNELS : channels; os_memcpy(cl->channel, pos, cl->channels); @@ -384,7 +384,7 @@ void p2p_reselect_channel(struct p2p_data *p2p, unsigned int i; const int op_classes_5ghz[] = { 124, 125, 115, 0 }; const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; - const int op_classes_vht[] = { 128, 0 }; + const int op_classes_vht[] = { 128, 129, 130, 0 }; if (p2p->own_freq_preference > 0 && p2p_freq_to_channel(p2p->own_freq_preference, @@ -901,6 +901,14 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, return; } + if (dev->go_neg_req_sent && + (dev->flags & P2P_DEV_PEER_WAITING_RESPONSE)) { + p2p_dbg(p2p, + "Do not reply since peer is waiting for us to start a new GO Negotiation and GO Neg Request already sent"); + p2p_parse_free(&msg); + return; + } + go = p2p_go_det(p2p->go_intent, *msg.go_intent); if (go < 0) { p2p_dbg(p2p, "Incompatible GO Intent"); @@ -1052,7 +1060,7 @@ fail: P2P_PENDING_GO_NEG_RESPONSE_FAILURE; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 500) < 0) { + wpabuf_head(resp), wpabuf_len(resp), 100) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } @@ -1260,6 +1268,11 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, dev->client_timeout = msg.config_timeout[1]; } + if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + } + if (!msg.operating_channel && !go) { /* * Note: P2P Client may omit Operating Channel attribute to @@ -1386,7 +1399,7 @@ fail: if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa, wpabuf_head(dev->go_neg_conf), - wpabuf_len(dev->go_neg_conf), 200) < 0) { + wpabuf_len(dev->go_neg_conf), 50) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); p2p_go_neg_failed(p2p, -1); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c index 0d66993465686..051b4e391505e 100644 --- a/src/p2p/p2p_group.c +++ b/src/p2p/p2p_group.c @@ -155,7 +155,8 @@ static void p2p_group_add_common_ies(struct p2p_group *group, group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; if (group->num_members >= group->cfg->max_clients) group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT; - group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION; + if (group->cfg->ip_addr_alloc) + group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION; p2p_buf_add_capability(ie, dev_capab, group_capab); } @@ -296,14 +297,14 @@ static int wifi_display_add_dev_info_descr(struct wpabuf *buf, os_memset(zero_addr, 0, ETH_ALEN); pos = wpabuf_head_u8(m->wfd_ie); end = pos + wpabuf_len(m->wfd_ie); - while (pos + 1 < end) { + while (end - pos >= 3) { u8 id; u16 len; id = *pos++; len = WPA_GET_BE16(pos); pos += 2; - if (pos + len > end) + if (len > end - pos) break; switch (id) { @@ -849,6 +850,20 @@ static struct p2p_group_member * p2p_group_get_client(struct p2p_group *group, } +const u8 * p2p_group_get_client_interface_addr(struct p2p_group *group, + const u8 *dev_addr) +{ + struct p2p_group_member *m; + + if (!group) + return NULL; + m = p2p_group_get_client(group, dev_addr); + if (m) + return m->addr; + return NULL; +} + + static struct p2p_group_member * p2p_group_get_client_iface( struct p2p_group *group, const u8 *interface_addr) { @@ -1097,7 +1112,7 @@ int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, struct p2p_device *dev; dev = p2p_get_device(group->p2p, m->dev_addr); - if (!dev) + if (!dev || dev->channels.reg_classes == 0) continue; p2p_channels_intersect(&intersect, &dev->channels, &res); diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 0ce4058fe3e69..47524d4991a5a 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -308,6 +308,18 @@ struct p2p_data { */ int num_p2p_sd_queries; + /** + * sd_query_no_ack - The first peer (Dev Addr) that did not ACK SD Query + * + * This is used to track the first peer that did not ACK an SD Query + * within a single P2P Search iteration. All zeros address means no such + * peer was yet seen. This information is used to allow a new Listen and + * Search phases to be once every pending SD Query has been sent once to + * each peer instead of looping all pending attempts continuously until + * running out of retry maximums. + */ + u8 sd_query_no_ack[ETH_ALEN]; + /* GO Negotiation data */ /** @@ -691,6 +703,8 @@ int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class, u8 *op_channel); /* p2p_parse.c */ +void p2p_copy_filter_devname(char *dst, size_t dst_len, + const void *src, size_t src_len); int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg); int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg); int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg); diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index 108e5b7f93e4d..bbba001a7c930 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -284,7 +284,7 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, if (!p2p_channels_includes(&intersection, reg_class, channel)) { - p2p_dbg(p2p, "forced freq %d MHz not in the supported channels interaction", + p2p_dbg(p2p, "forced freq %d MHz not in the supported channels intersection", op_freq); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; @@ -418,7 +418,7 @@ fail: p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + wpabuf_head(resp), wpabuf_len(resp), 50) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c index bd1e68bd42417..5d2299cb25c2e 100644 --- a/src/p2p/p2p_parse.c +++ b/src/p2p/p2p_parse.c @@ -15,11 +15,29 @@ #include "p2p_i.h" +void p2p_copy_filter_devname(char *dst, size_t dst_len, + const void *src, size_t src_len) +{ + size_t i; + + if (src_len >= dst_len) + src_len = dst_len - 1; + os_memcpy(dst, src, src_len); + dst[src_len] = '\0'; + for (i = 0; i < src_len; i++) { + if (dst[i] == '\0') + break; + if (is_ctrl_char(dst[i])) + dst[i] = '_'; + } +} + + static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, struct p2p_message *msg) { const u8 *pos; - size_t i, nlen; + u16 nlen; char devtype[WPS_DEV_TYPE_BUFSIZE]; switch (id) { @@ -149,21 +167,14 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, pos += 2; nlen = WPA_GET_BE16(pos); pos += 2; - if (data + len - pos < (int) nlen || - nlen > WPS_DEV_NAME_MAX_LEN) { + if (nlen > data + len - pos || nlen > WPS_DEV_NAME_MAX_LEN) { wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name " - "length %d (buf len %d)", (int) nlen, + "length %u (buf len %d)", nlen, (int) (data + len - pos)); return -1; } - os_memcpy(msg->device_name, pos, nlen); - msg->device_name[nlen] = '\0'; - for (i = 0; i < nlen; i++) { - if (msg->device_name[i] == '\0') - break; - if (is_ctrl_char(msg->device_name[i])) - msg->device_name[i] = '_'; - } + p2p_copy_filter_devname(msg->device_name, + sizeof(msg->device_name), pos, nlen); wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR " primary device type %s device name '%s' " "config methods 0x%x", @@ -637,49 +648,48 @@ int p2p_group_info_parse(const u8 *gi, size_t gi_len, gend = gi + gi_len; while (g < gend) { struct p2p_client_info *cli; - const u8 *t, *cend; - int count; + const u8 *cend; + u16 count; + u8 len; cli = &info->client[info->num_clients]; - cend = g + 1 + g[0]; - if (cend > gend) + len = *g++; + if (len > gend - g || len < 2 * ETH_ALEN + 1 + 2 + 8 + 1) return -1; /* invalid data */ + cend = g + len; /* g at start of P2P Client Info Descriptor */ - /* t at Device Capability Bitmap */ - t = g + 1 + 2 * ETH_ALEN; - if (t > cend) - return -1; /* invalid data */ - cli->p2p_device_addr = g + 1; - cli->p2p_interface_addr = g + 1 + ETH_ALEN; - cli->dev_capab = t[0]; - - if (t + 1 + 2 + 8 + 1 > cend) - return -1; /* invalid data */ - - cli->config_methods = WPA_GET_BE16(&t[1]); - cli->pri_dev_type = &t[3]; - - t += 1 + 2 + 8; - /* t at Number of Secondary Device Types */ - cli->num_sec_dev_types = *t++; - if (t + 8 * cli->num_sec_dev_types > cend) + cli->p2p_device_addr = g; + g += ETH_ALEN; + cli->p2p_interface_addr = g; + g += ETH_ALEN; + cli->dev_capab = *g++; + + cli->config_methods = WPA_GET_BE16(g); + g += 2; + cli->pri_dev_type = g; + g += 8; + + /* g at Number of Secondary Device Types */ + len = *g++; + if (8 * len > cend - g) return -1; /* invalid data */ - cli->sec_dev_types = t; - t += 8 * cli->num_sec_dev_types; + cli->num_sec_dev_types = len; + cli->sec_dev_types = g; + g += 8 * len; - /* t at Device Name in WPS TLV format */ - if (t + 2 + 2 > cend) + /* g at Device Name in WPS TLV format */ + if (cend - g < 2 + 2) return -1; /* invalid data */ - if (WPA_GET_BE16(t) != ATTR_DEV_NAME) + if (WPA_GET_BE16(g) != ATTR_DEV_NAME) return -1; /* invalid Device Name TLV */ - t += 2; - count = WPA_GET_BE16(t); - t += 2; - if (count > cend - t) + g += 2; + count = WPA_GET_BE16(g); + g += 2; + if (count > cend - g) return -1; /* invalid Device Name TLV */ if (count >= WPS_DEV_NAME_MAX_LEN) count = WPS_DEV_NAME_MAX_LEN; - cli->dev_name = (const char *) t; + cli->dev_name = (const char *) g; cli->dev_name_len = count; g = cend; diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c index 890094551821a..93a0535f873a0 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -40,21 +40,31 @@ static void p2p_build_wps_ie_config_methods(struct wpabuf *buf, } -static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf) +static void p2ps_add_new_group_info(struct p2p_data *p2p, + struct p2p_device *dev, + struct wpabuf *buf) { int found; u8 intended_addr[ETH_ALEN]; u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int group_iface; + unsigned int force_freq; if (!p2p->cfg->get_go_info) return; found = p2p->cfg->get_go_info( p2p->cfg->cb_ctx, intended_addr, ssid, - &ssid_len, &group_iface); + &ssid_len, &group_iface, &force_freq); if (found) { + if (force_freq > 0) { + p2p->p2ps_prov->force_freq = force_freq; + p2p->p2ps_prov->pref_freq = 0; + + if (dev) + p2p_prepare_channel(p2p, dev, force_freq, 0, 0); + } p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, ssid, ssid_len); @@ -92,62 +102,62 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, size_t ssid_len; u8 go_dev_addr[ETH_ALEN]; u8 intended_addr[ETH_ALEN]; + int follow_on_req_fail = prov->status >= 0 && + prov->status != P2P_SC_SUCCESS_DEFERRED; /* If we might be explicite group owner, add GO details */ - if (prov->conncap & (P2PS_SETUP_GROUP_OWNER | - P2PS_SETUP_NEW)) - p2ps_add_new_group_info(p2p, buf); + if (!follow_on_req_fail && + (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW))) + p2ps_add_new_group_info(p2p, dev, buf); if (prov->status >= 0) p2p_buf_add_status(buf, (u8) prov->status); else prov->method = config_methods; - if (p2p->cfg->get_persistent_group) { - shared_group = p2p->cfg->get_persistent_group( - p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0, - go_dev_addr, ssid, &ssid_len, intended_addr); - } - - /* Add Operating Channel if conncap includes GO */ - if (shared_group || - (prov->conncap & (P2PS_SETUP_GROUP_OWNER | - P2PS_SETUP_NEW))) { - u8 tmp; + if (!follow_on_req_fail) { + if (p2p->cfg->get_persistent_group) { + shared_group = p2p->cfg->get_persistent_group( + p2p->cfg->cb_ctx, dev->info.p2p_device_addr, + NULL, 0, go_dev_addr, ssid, &ssid_len, + intended_addr); + } - p2p_go_select_channel(p2p, dev, &tmp); + if (shared_group || + (prov->conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_NEW))) + p2p_buf_add_channel_list(buf, p2p->cfg->country, + &p2p->channels); - if (p2p->op_reg_class && p2p->op_channel) + if ((shared_group && !is_zero_ether_addr(intended_addr)) || + (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW))) p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); - else - p2p_buf_add_operating_channel(buf, p2p->cfg->country, - p2p->cfg->op_reg_class, - p2p->cfg->op_channel); } - p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->cfg->channels); - - if (prov->info[0]) + if (prov->status < 0 && prov->info[0]) p2p_buf_add_session_info(buf, prov->info); - p2p_buf_add_connection_capability(buf, prov->conncap); + if (!follow_on_req_fail) + p2p_buf_add_connection_capability(buf, prov->conncap); p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac); - if (shared_group || prov->conncap == P2PS_SETUP_NEW || - prov->conncap == - (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) || - prov->conncap == - (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) { - /* Add Config Timeout */ - p2p_buf_add_config_timeout(buf, p2p->go_timeout, - p2p->client_timeout); - } + if (!follow_on_req_fail) { + if (shared_group || prov->conncap == P2PS_SETUP_NEW || + prov->conncap == + (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) || + prov->conncap == + (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) { + /* Add Config Timeout */ + p2p_buf_add_config_timeout(buf, p2p->go_timeout, + p2p->client_timeout); + } - p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class, - p2p->cfg->channel); + p2p_buf_add_listen_channel(buf, p2p->cfg->country, + p2p->cfg->reg_class, + p2p->cfg->channel); + } p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); @@ -285,6 +295,11 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, u8 *len = p2p_buf_add_ie_hdr(buf); struct p2ps_provision *prov = p2p->p2ps_prov; u8 group_capab; + u8 conncap = 0; + + if (status == P2P_SC_SUCCESS || + status == P2P_SC_SUCCESS_DEFERRED) + conncap = prov->conncap; if (!status && prov->status != -1) status = prov->status; @@ -301,7 +316,7 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, group_capab); p2p_buf_add_device_info(buf, p2p, NULL); - if (persist_ssid && p2p->cfg->get_persistent_group && + if (persist_ssid && p2p->cfg->get_persistent_group && dev && (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED)) { u8 ssid[SSID_MAX_LEN]; @@ -323,16 +338,11 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, } } - if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER)) - p2ps_add_new_group_info(p2p, buf); + if (!persist && (conncap & P2PS_SETUP_GROUP_OWNER)) + p2ps_add_new_group_info(p2p, dev, buf); /* Add Operating Channel if conncap indicates GO */ - if (persist || (prov->conncap & P2PS_SETUP_GROUP_OWNER)) { - u8 tmp; - - if (dev) - p2p_go_select_channel(p2p, dev, &tmp); - + if (persist || (conncap & P2PS_SETUP_GROUP_OWNER)) { if (p2p->op_reg_class && p2p->op_channel) p2p_buf_add_operating_channel( buf, p2p->cfg->country, @@ -345,17 +355,20 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, p2p->cfg->op_channel); } - p2p_buf_add_channel_list(buf, p2p->cfg->country, - &p2p->cfg->channels); + if (persist || + (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER))) + p2p_buf_add_channel_list(buf, p2p->cfg->country, + &p2p->channels); - if (!persist && (status == P2P_SC_SUCCESS || - status == P2P_SC_SUCCESS_DEFERRED)) - p2p_buf_add_connection_capability(buf, prov->conncap); + if (!persist && conncap) + p2p_buf_add_connection_capability(buf, conncap); p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac); - p2p_buf_add_config_timeout(buf, p2p->go_timeout, - p2p->client_timeout); + if (persist || + (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER))) + p2p_buf_add_config_timeout(buf, p2p->go_timeout, + p2p->client_timeout); p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); @@ -427,6 +440,117 @@ static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask) } +/* Check if the message contains a valid P2PS PD Request */ +static int p2ps_validate_pd_req(struct p2p_data *p2p, struct p2p_message *msg, + const u8 *addr) +{ + u8 group_id = 0; + u8 intended_addr = 0; + u8 operating_channel = 0; + u8 channel_list = 0; + u8 config_timeout = 0; + u8 listen_channel = 0; + +#define P2PS_PD_REQ_CHECK(_val, _attr) \ +do { \ + if ((_val) && !msg->_attr) { \ + p2p_dbg(p2p, "Not P2PS PD Request. Missing %s", #_attr); \ + return -1; \ + } \ +} while (0) + + P2PS_PD_REQ_CHECK(1, adv_id); + P2PS_PD_REQ_CHECK(1, session_id); + P2PS_PD_REQ_CHECK(1, session_mac); + P2PS_PD_REQ_CHECK(1, adv_mac); + P2PS_PD_REQ_CHECK(1, capability); + P2PS_PD_REQ_CHECK(1, p2p_device_info); + P2PS_PD_REQ_CHECK(1, feature_cap); + + /* + * We don't need to check Connection Capability, Persistent Group, + * and related attributes for follow-on PD Request with a status + * other than SUCCESS_DEFERRED. + */ + if (msg->status && *msg->status != P2P_SC_SUCCESS_DEFERRED) + return 0; + + P2PS_PD_REQ_CHECK(1, conn_cap); + + /* + * Note 1: A feature capability attribute structure can be changed + * in the future. The assumption is that such modifications are + * backward compatible, therefore we allow processing of msg.feature_cap + * exceeding the size of the p2ps_feature_capab structure. + * Note 2: Verification of msg.feature_cap_len below has to be changed + * to allow 2 byte feature capability processing if + * struct p2ps_feature_capab is extended to include additional fields + * and it affects the structure size. + */ + if (msg->feature_cap_len < sizeof(struct p2ps_feature_capab)) { + p2p_dbg(p2p, "P2PS: Invalid feature capability len"); + return -1; + } + + switch (*msg->conn_cap) { + case P2PS_SETUP_NEW: + group_id = 1; + intended_addr = 1; + operating_channel = 1; + channel_list = 1; + config_timeout = 1; + listen_channel = 1; + break; + case P2PS_SETUP_CLIENT: + channel_list = 1; + listen_channel = 1; + break; + case P2PS_SETUP_GROUP_OWNER: + group_id = 1; + intended_addr = 1; + operating_channel = 1; + break; + case P2PS_SETUP_NEW | P2PS_SETUP_GROUP_OWNER: + group_id = 1; + operating_channel = 1; + intended_addr = 1; + channel_list = 1; + config_timeout = 1; + break; + case P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER: + group_id = 1; + intended_addr = 1; + operating_channel = 1; + channel_list = 1; + config_timeout = 1; + break; + default: + p2p_dbg(p2p, "Invalid P2PS PD connection capability"); + return -1; + } + + if (msg->persistent_dev) { + channel_list = 1; + config_timeout = 1; + if (os_memcmp(msg->persistent_dev, addr, ETH_ALEN) == 0) { + intended_addr = 1; + operating_channel = 1; + } + } + + P2PS_PD_REQ_CHECK(group_id, group_id); + P2PS_PD_REQ_CHECK(intended_addr, intended_addr); + P2PS_PD_REQ_CHECK(operating_channel, operating_channel); + P2PS_PD_REQ_CHECK(channel_list, channel_list); + P2PS_PD_REQ_CHECK(config_timeout, config_timeout); + P2PS_PD_REQ_CHECK(listen_channel, listen_channel); + +#undef P2PS_PD_REQ_CHECK + + return 0; +} + + void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { @@ -440,14 +564,16 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, u8 conncap = P2PS_SETUP_NEW; u8 auto_accept = 0; u32 session_id = 0; - u8 session_mac[ETH_ALEN]; - u8 adv_mac[ETH_ALEN]; + u8 session_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + u8 adv_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; const u8 *group_mac; int passwd_id = DEV_PW_DEFAULT; u16 config_methods; u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; struct p2ps_feature_capab resp_fcap = { 0, 0 }; - struct p2ps_feature_capab *req_fcap; + struct p2ps_feature_capab *req_fcap = NULL; + u8 remote_conncap; + u16 method; if (p2p_parse(data, len, &msg)) return; @@ -466,300 +592,431 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, 0)) { p2p_dbg(p2p, "Provision Discovery Request add device failed " MACSTR, MAC2STR(sa)); + goto out; + } + + if (!dev) { + dev = p2p_get_device(p2p, sa); + if (!dev) { + p2p_dbg(p2p, + "Provision Discovery device not found " + MACSTR, MAC2STR(sa)); + goto out; + } } } else if (msg.wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } - if (msg.adv_id) - allowed_config_methods |= WPS_CONFIG_P2PS; - else + if (!msg.adv_id) { allowed_config_methods |= WPS_CONFIG_PUSHBUTTON; + if (!(msg.wps_config_methods & allowed_config_methods)) { + p2p_dbg(p2p, + "Unsupported Config Methods in Provision Discovery Request"); + goto out; + } - if (!(msg.wps_config_methods & allowed_config_methods)) { - p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request"); - goto out; - } + /* Legacy (non-P2PS) - Unknown groups allowed for P2PS */ + if (msg.group_id) { + size_t i; - /* Legacy (non-P2PS) - Unknown groups allowed for P2PS */ - if (!msg.adv_id && msg.group_id) { - size_t i; - for (i = 0; i < p2p->num_groups; i++) { - if (p2p_group_is_group_id_match(p2p->groups[i], - msg.group_id, - msg.group_id_len)) - break; + for (i = 0; i < p2p->num_groups; i++) { + if (p2p_group_is_group_id_match( + p2p->groups[i], + msg.group_id, msg.group_id_len)) + break; + } + if (i == p2p->num_groups) { + p2p_dbg(p2p, + "PD request for unknown P2P Group ID - reject"); + goto out; + } } - if (i == p2p->num_groups) { - p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject"); + } else { + allowed_config_methods |= WPS_CONFIG_P2PS; + + /* + * Set adv_id here, so in case of an error, a P2PS PD Response + * will be sent. + */ + adv_id = WPA_GET_LE32(msg.adv_id); + if (p2ps_validate_pd_req(p2p, &msg, sa) < 0) { + reject = P2P_SC_FAIL_INVALID_PARAMS; goto out; } - } - if (dev) { - dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | - P2P_DEV_PD_PEER_KEYPAD | - P2P_DEV_PD_PEER_P2PS); + req_fcap = (struct p2ps_feature_capab *) msg.feature_cap; - /* Remove stale persistent groups */ - if (p2p->cfg->remove_stale_groups) { - p2p->cfg->remove_stale_groups( - p2p->cfg->cb_ctx, dev->info.p2p_device_addr, - msg.persistent_dev, - msg.persistent_ssid, msg.persistent_ssid_len); + os_memcpy(session_mac, msg.session_mac, ETH_ALEN); + os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); + + session_id = WPA_GET_LE32(msg.session_id); + + if (msg.conn_cap) + conncap = *msg.conn_cap; + + /* + * We need to verify a P2PS config methog in an initial PD + * request or in a follow-on PD request with the status + * SUCCESS_DEFERRED. + */ + if ((!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) && + !(msg.wps_config_methods & allowed_config_methods)) { + p2p_dbg(p2p, + "Unsupported Config Methods in Provision Discovery Request"); + goto out; } + + /* + * TODO: since we don't support multiple PD, reject PD request + * if we are in the middle of P2PS PD with some other peer + */ } + + dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | + P2P_DEV_PD_PEER_KEYPAD | + P2P_DEV_PD_PEER_P2PS); + if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) { p2p_dbg(p2p, "Peer " MACSTR " requested us to show a PIN on display", MAC2STR(sa)); - if (dev) - dev->flags |= P2P_DEV_PD_PEER_KEYPAD; + dev->flags |= P2P_DEV_PD_PEER_KEYPAD; passwd_id = DEV_PW_USER_SPECIFIED; } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { p2p_dbg(p2p, "Peer " MACSTR " requested us to write its PIN using keypad", MAC2STR(sa)); - if (dev) - dev->flags |= P2P_DEV_PD_PEER_DISPLAY; + dev->flags |= P2P_DEV_PD_PEER_DISPLAY; passwd_id = DEV_PW_REGISTRAR_SPECIFIED; } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) { p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN", MAC2STR(sa)); - if (dev) - dev->flags |= P2P_DEV_PD_PEER_P2PS; + dev->flags |= P2P_DEV_PD_PEER_P2PS; passwd_id = DEV_PW_P2PS_DEFAULT; } - reject = P2P_SC_SUCCESS; + /* Remove stale persistent groups */ + if (p2p->cfg->remove_stale_groups) { + p2p->cfg->remove_stale_groups( + p2p->cfg->cb_ctx, dev->info.p2p_device_addr, + msg.persistent_dev, + msg.persistent_ssid, msg.persistent_ssid_len); + } - os_memset(session_mac, 0, ETH_ALEN); - os_memset(adv_mac, 0, ETH_ALEN); + reject = P2P_SC_SUCCESS; - /* Note 1: A feature capability attribute structure can be changed - * in the future. The assumption is that such modifications are - * backwards compatible, therefore we allow processing of - * msg.feature_cap exceeding the size of the p2ps_feature_capab - * structure. - * Note 2: Vverification of msg.feature_cap_len below has to be changed - * to allow 2 byte feature capability processing if struct - * p2ps_feature_capab is extended to include additional fields and it - * affects the structure size. + /* + * End of a legacy P2P PD Request processing, from this point continue + * with P2PS one. */ - if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac && - msg.feature_cap && msg.feature_cap_len >= sizeof(*req_fcap) && - (msg.status || msg.conn_cap)) { - u8 remote_conncap; + if (!msg.adv_id) + goto out; - req_fcap = (struct p2ps_feature_capab *) msg.feature_cap; + remote_conncap = conncap; - os_memcpy(session_mac, msg.session_mac, ETH_ALEN); - os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); + if (!msg.status) { + unsigned int forced_freq, pref_freq; - session_id = WPA_GET_LE32(msg.session_id); - adv_id = WPA_GET_LE32(msg.adv_id); - - if (!msg.status) - p2ps_adv = p2p_service_p2ps_id(p2p, adv_id); + if (os_memcmp(p2p->cfg->dev_addr, msg.adv_mac, ETH_ALEN)) { + p2p_dbg(p2p, + "P2PS PD adv mac does not match the local one"); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + goto out; + } - p2p_dbg(p2p, "adv_id: %x - p2ps_adv - %p", adv_id, p2ps_adv); + p2ps_adv = p2p_service_p2ps_id(p2p, adv_id); + if (!p2ps_adv) { + p2p_dbg(p2p, "P2PS PD invalid adv_id=0x%X", adv_id); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + goto out; + } + p2p_dbg(p2p, "adv_id: 0x%X, p2ps_adv: %p", adv_id, p2ps_adv); - if (msg.conn_cap) - conncap = *msg.conn_cap; - remote_conncap = conncap; + auto_accept = p2ps_adv->auto_accept; + conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx, + conncap, auto_accept, + &forced_freq, + &pref_freq); - if (p2ps_adv) { - auto_accept = p2ps_adv->auto_accept; - conncap = p2p->cfg->p2ps_group_capability( - p2p->cfg->cb_ctx, conncap, auto_accept); + p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d", + auto_accept, remote_conncap, conncap); - p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d", - auto_accept, remote_conncap, conncap); + p2p_prepare_channel(p2p, dev, forced_freq, pref_freq, 0); - resp_fcap.cpt = - p2ps_own_preferred_cpt(p2ps_adv->cpt_priority, + resp_fcap.cpt = p2ps_own_preferred_cpt(p2ps_adv->cpt_priority, req_fcap->cpt); + p2p_dbg(p2p, "cpt: service:0x%x remote:0x%x result:0x%x", + p2ps_adv->cpt_mask, req_fcap->cpt, resp_fcap.cpt); + + if (!resp_fcap.cpt) { + p2p_dbg(p2p, + "Incompatible P2PS feature capability CPT bitmask"); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if (p2ps_adv->config_methods && + !(msg.wps_config_methods & + p2ps_adv->config_methods)) { p2p_dbg(p2p, - "cpt: service:0x%x remote:0x%x result:0x%x", - p2ps_adv->cpt_mask, req_fcap->cpt, - resp_fcap.cpt); + "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)", + p2ps_adv->config_methods, + msg.wps_config_methods); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if (!p2ps_adv->state) { + p2p_dbg(p2p, "P2PS state unavailable"); + reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } else if (!conncap) { + p2p_dbg(p2p, "Conncap resolution failed"); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } - if (!resp_fcap.cpt) { - p2p_dbg(p2p, - "Incompatible P2PS feature capability CPT bitmask"); - reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } else if (p2ps_adv->config_methods && - !(msg.wps_config_methods & - p2ps_adv->config_methods)) { - p2p_dbg(p2p, - "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)", - p2ps_adv->config_methods, - msg.wps_config_methods); - reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } else if (!p2ps_adv->state) { - p2p_dbg(p2p, "P2PS state unavailable"); - reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; - } else if (!conncap) { - p2p_dbg(p2p, "Conncap resolution failed"); - reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } + if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { + p2p_dbg(p2p, "Keypad - always defer"); + auto_accept = 0; + } - if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { - p2p_dbg(p2p, "Keypad - always defer"); - auto_accept = 0; + if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) || + msg.persistent_dev) && conncap != P2PS_SETUP_NEW && + msg.channel_list && msg.channel_list_len && + p2p_peer_channels_check(p2p, &p2p->channels, dev, + msg.channel_list, + msg.channel_list_len) < 0) { + p2p_dbg(p2p, + "No common channels - force deferred flow"); + auto_accept = 0; + } + + if (((remote_conncap & P2PS_SETUP_GROUP_OWNER) || + msg.persistent_dev) && msg.operating_channel) { + struct p2p_channels intersect; + + /* + * There are cases where only the operating channel is + * provided. This requires saving the channel as the + * supported channel list, and verifying that it is + * supported. + */ + if (dev->channels.reg_classes == 0 || + !p2p_channels_includes(&dev->channels, + msg.operating_channel[3], + msg.operating_channel[4])) { + struct p2p_channels *ch = &dev->channels; + + os_memset(ch, 0, sizeof(*ch)); + ch->reg_class[0].reg_class = + msg.operating_channel[3]; + ch->reg_class[0].channel[0] = + msg.operating_channel[4]; + ch->reg_class[0].channels = 1; + ch->reg_classes = 1; } - if (auto_accept || reject != P2P_SC_SUCCESS) { - struct p2ps_provision *tmp; - - if (reject == P2P_SC_SUCCESS && !conncap) { - reject = - P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } - - if (p2ps_setup_p2ps_prov( - p2p, adv_id, session_id, - msg.wps_config_methods, - session_mac, adv_mac) < 0) { - reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; - goto out; - } - - tmp = p2p->p2ps_prov; - if (conncap) { - tmp->conncap = conncap; - tmp->status = P2P_SC_SUCCESS; - } else { - tmp->conncap = auto_accept; - tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } - - if (reject != P2P_SC_SUCCESS) - goto out; + p2p_channels_intersect(&p2p->channels, &dev->channels, + &intersect); + + if (intersect.reg_classes == 0) { + p2p_dbg(p2p, + "No common channels - force deferred flow"); + auto_accept = 0; } - } else if (!msg.status) { - reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - goto out; } - if (!msg.status && !auto_accept && - (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) { + if (auto_accept || reject != P2P_SC_SUCCESS) { struct p2ps_provision *tmp; - if (!conncap) { - reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - goto out; - } - if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id, msg.wps_config_methods, session_mac, adv_mac) < 0) { reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; goto out; } + tmp = p2p->p2ps_prov; - reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; - tmp->status = reject; + tmp->force_freq = forced_freq; + tmp->pref_freq = pref_freq; + if (conncap) { + tmp->conncap = conncap; + tmp->status = P2P_SC_SUCCESS; + } else { + tmp->conncap = auto_accept; + tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } + + if (reject != P2P_SC_SUCCESS) + goto out; } + } - if (msg.status) { - if (*msg.status && - *msg.status != P2P_SC_SUCCESS_DEFERRED) { - reject = *msg.status; - } else if (*msg.status == P2P_SC_SUCCESS_DEFERRED && - p2p->p2ps_prov) { - u16 method = p2p->p2ps_prov->method; + if (!msg.status && !auto_accept && + (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) { + struct p2ps_provision *tmp; - conncap = p2p->cfg->p2ps_group_capability( - p2p->cfg->cb_ctx, remote_conncap, - p2p->p2ps_prov->conncap); + if (!conncap) { + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + goto out; + } - p2p_dbg(p2p, - "Conncap: local:%d remote:%d result:%d", - p2p->p2ps_prov->conncap, - remote_conncap, conncap); + if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id, + msg.wps_config_methods, + session_mac, adv_mac) < 0) { + reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + goto out; + } + tmp = p2p->p2ps_prov; + reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + tmp->status = reject; + } - resp_fcap.cpt = p2ps_own_preferred_cpt( - p2p->p2ps_prov->cpt_priority, - req_fcap->cpt); + /* Not a P2PS Follow-on PD */ + if (!msg.status) + goto out; - p2p_dbg(p2p, - "cpt: local:0x%x remote:0x%x result:0x%x", - p2p->p2ps_prov->cpt_mask, - req_fcap->cpt, resp_fcap.cpt); + if (*msg.status && *msg.status != P2P_SC_SUCCESS_DEFERRED) { + reject = *msg.status; + goto out; + } - /* - * Ensure that if we asked for PIN originally, - * our method is consistent with original - * request. - */ - if (method & WPS_CONFIG_DISPLAY) - method = WPS_CONFIG_KEYPAD; - else if (method & WPS_CONFIG_KEYPAD) - method = WPS_CONFIG_DISPLAY; - - if (!conncap || - !(msg.wps_config_methods & method)) { - /* - * Reject this "Deferred Accept* - * if incompatible conncap or method - */ - reject = - P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } else if (!resp_fcap.cpt) { - p2p_dbg(p2p, - "Incompatible P2PS feature capability CPT bitmask"); - reject = - P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } else { - reject = P2P_SC_SUCCESS; - } - - p2p->p2ps_prov->status = reject; - p2p->p2ps_prov->conncap = conncap; - } - } + if (*msg.status != P2P_SC_SUCCESS_DEFERRED || !p2p->p2ps_prov) + goto out; + + if (p2p->p2ps_prov->adv_id != adv_id || + os_memcmp(p2p->p2ps_prov->adv_mac, msg.adv_mac, ETH_ALEN)) { + p2p_dbg(p2p, + "P2PS Follow-on PD with mismatch Advertisement ID/MAC"); + goto out; } + if (p2p->p2ps_prov->session_id != session_id || + os_memcmp(p2p->p2ps_prov->session_mac, msg.session_mac, ETH_ALEN)) { + p2p_dbg(p2p, "P2PS Follow-on PD with mismatch Session ID/MAC"); + goto out; + } + + method = p2p->p2ps_prov->method; + + conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx, + remote_conncap, + p2p->p2ps_prov->conncap, + &p2p->p2ps_prov->force_freq, + &p2p->p2ps_prov->pref_freq); + + resp_fcap.cpt = p2ps_own_preferred_cpt(p2p->p2ps_prov->cpt_priority, + req_fcap->cpt); + + p2p_dbg(p2p, "cpt: local:0x%x remote:0x%x result:0x%x", + p2p->p2ps_prov->cpt_mask, req_fcap->cpt, resp_fcap.cpt); + + p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq, + p2p->p2ps_prov->pref_freq, 0); + + /* + * Ensure that if we asked for PIN originally, our method is consistent + * with original request. + */ + if (method & WPS_CONFIG_DISPLAY) + method = WPS_CONFIG_KEYPAD; + else if (method & WPS_CONFIG_KEYPAD) + method = WPS_CONFIG_DISPLAY; + + if (!conncap || !(msg.wps_config_methods & method)) { + /* + * Reject this "Deferred Accept* + * if incompatible conncap or method + */ + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if (!resp_fcap.cpt) { + p2p_dbg(p2p, + "Incompatible P2PS feature capability CPT bitmask"); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) || + msg.persistent_dev) && conncap != P2PS_SETUP_NEW && + msg.channel_list && msg.channel_list_len && + p2p_peer_channels_check(p2p, &p2p->channels, dev, + msg.channel_list, + msg.channel_list_len) < 0) { + p2p_dbg(p2p, + "No common channels in Follow-On Provision Discovery Request"); + reject = P2P_SC_FAIL_NO_COMMON_CHANNELS; + } else { + reject = P2P_SC_SUCCESS; + } + + dev->oper_freq = 0; + if (reject == P2P_SC_SUCCESS || reject == P2P_SC_SUCCESS_DEFERRED) { + u8 tmp; + + if (msg.operating_channel) + dev->oper_freq = + p2p_channel_to_freq(msg.operating_channel[3], + msg.operating_channel[4]); + + if ((conncap & P2PS_SETUP_GROUP_OWNER) && + p2p_go_select_channel(p2p, dev, &tmp) < 0) + reject = P2P_SC_FAIL_NO_COMMON_CHANNELS; + } + + p2p->p2ps_prov->status = reject; + p2p->p2ps_prov->conncap = conncap; + out: if (reject == P2P_SC_SUCCESS || reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) config_methods = msg.wps_config_methods; else config_methods = 0; - resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token, reject, - config_methods, adv_id, - msg.group_id, msg.group_id_len, - msg.persistent_ssid, - msg.persistent_ssid_len, - (const u8 *) &resp_fcap, - sizeof(resp_fcap)); - if (resp == NULL) { - p2p_parse_free(&msg); - return; - } - p2p_dbg(p2p, "Sending Provision Discovery Response"); - if (rx_freq > 0) - freq = rx_freq; - else - freq = p2p_channel_to_freq(p2p->cfg->reg_class, - p2p->cfg->channel); - if (freq < 0) { - p2p_dbg(p2p, "Unknown regulatory class/channel"); + + /* + * Send PD Response for an initial PD Request or for follow-on + * PD Request with P2P_SC_SUCCESS_DEFERRED status. + */ + if (!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) { + resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token, + reject, config_methods, adv_id, + msg.group_id, msg.group_id_len, + msg.persistent_ssid, + msg.persistent_ssid_len, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); + if (!resp) { + p2p_parse_free(&msg); + return; + } + p2p_dbg(p2p, "Sending Provision Discovery Response"); + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Unknown regulatory class/channel"); + wpabuf_free(resp); + p2p_parse_free(&msg); + return; + } + p2p->pending_action_state = P2P_PENDING_PD_RESPONSE; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), + 50) < 0) + p2p_dbg(p2p, "Failed to send Action frame"); + else + p2p->send_action_in_progress = 1; + wpabuf_free(resp); + } + + if (!dev) { p2p_parse_free(&msg); return; } - p2p->pending_action_state = P2P_PENDING_PD_RESPONSE; - if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, - p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { - p2p_dbg(p2p, "Failed to send Action frame"); - } else - p2p->send_action_in_progress = 1; - wpabuf_free(resp); + freq = 0; + if (reject == P2P_SC_SUCCESS && conncap == P2PS_SETUP_GROUP_OWNER) { + freq = p2p_channel_to_freq(p2p->op_reg_class, + p2p->op_channel); + if (freq < 0) + freq = 0; + } if (!p2p->cfg->p2ps_prov_complete) { /* Don't emit anything */ @@ -771,7 +1028,8 @@ out: NULL, adv_id, session_id, 0, 0, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 0, NULL, NULL, 0); + 0, 0, NULL, NULL, 0, freq, + NULL, 0); } else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { p2p->p2ps_prov->status = reject; @@ -784,7 +1042,8 @@ out: session_id, conncap, 0, msg.persistent_ssid, msg.persistent_ssid_len, 0, - 0, NULL, NULL, 0); + 0, NULL, NULL, 0, freq, + NULL, 0); else p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, @@ -796,7 +1055,8 @@ out: msg.persistent_ssid_len, 0, 0, NULL, (const u8 *) &resp_fcap, - sizeof(resp_fcap)); + sizeof(resp_fcap), freq, + NULL, 0); } else if (msg.status && p2p->p2ps_prov) { p2p->p2ps_prov->status = P2P_SC_SUCCESS; p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa, @@ -807,7 +1067,7 @@ out: msg.persistent_ssid_len, 0, 0, NULL, (const u8 *) &resp_fcap, - sizeof(resp_fcap)); + sizeof(resp_fcap), freq, NULL, 0); } else if (msg.status) { } else if (auto_accept && reject == P2P_SC_SUCCESS) { p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, @@ -818,7 +1078,11 @@ out: msg.persistent_ssid_len, 0, 0, NULL, (const u8 *) &resp_fcap, - sizeof(resp_fcap)); + sizeof(resp_fcap), freq, + msg.group_id ? + msg.group_id + ETH_ALEN : NULL, + msg.group_id ? + msg.group_id_len - ETH_ALEN : 0); } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && (!msg.session_info || !msg.session_info_len)) { p2p->p2ps_prov->method = msg.wps_config_methods; @@ -831,7 +1095,7 @@ out: msg.persistent_ssid_len, 0, 1, NULL, (const u8 *) &resp_fcap, - sizeof(resp_fcap)); + sizeof(resp_fcap), freq, NULL, 0); } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { size_t buf_len = msg.session_info_len; char *buf = os_malloc(2 * buf_len + 1); @@ -848,7 +1112,8 @@ out: session_id, conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, 0, 1, buf, - (const u8 *) &resp_fcap, sizeof(resp_fcap)); + (const u8 *) &resp_fcap, sizeof(resp_fcap), + freq, NULL, 0); os_free(buf); } @@ -898,7 +1163,7 @@ out: msg.group_id, msg.group_id_len); } - if (dev && reject == P2P_SC_SUCCESS) { + if (reject == P2P_SC_SUCCESS) { switch (config_methods) { case WPS_CONFIG_DISPLAY: dev->wps_prov_info = WPS_CONFIG_KEYPAD; @@ -1084,6 +1349,9 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, " with no pending request", MAC2STR(sa)); p2p_parse_free(&msg); return; + } else if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } if (dev->dialog_token != msg.dialog_token) { @@ -1148,17 +1416,71 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, passwd_id = DEV_PW_P2PS_DEFAULT; } - if ((msg.conn_cap || msg.persistent_dev) && - (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) && + if ((status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) && p2p->p2ps_prov) { + dev->oper_freq = 0; + + /* + * Save the reported channel list and operating frequency. + * Note that the specification mandates that the responder + * should include in the channel list only channels reported by + * the initiator, so this is only a sanity check, and if this + * fails the flow would continue, although it would probably + * fail. Same is true for the operating channel. + */ + if (msg.channel_list && msg.channel_list_len && + p2p_peer_channels_check(p2p, &p2p->channels, dev, + msg.channel_list, + msg.channel_list_len) < 0) + p2p_dbg(p2p, "P2PS PD Response - no common channels"); + + if (msg.operating_channel) { + if (p2p_channels_includes(&p2p->channels, + msg.operating_channel[3], + msg.operating_channel[4]) && + p2p_channels_includes(&dev->channels, + msg.operating_channel[3], + msg.operating_channel[4])) { + dev->oper_freq = + p2p_channel_to_freq( + msg.operating_channel[3], + msg.operating_channel[4]); + } else { + p2p_dbg(p2p, + "P2PS PD Response - invalid operating channel"); + } + } + if (p2p->cfg->p2ps_prov_complete) { + int freq = 0; + + if (conncap == P2PS_SETUP_GROUP_OWNER) { + u8 tmp; + + /* + * Re-select the operating channel as it is + * possible that original channel is no longer + * valid. This should not really fail. + */ + if (p2p_go_select_channel(p2p, dev, &tmp) < 0) + p2p_dbg(p2p, + "P2PS PD channel selection failed"); + + freq = p2p_channel_to_freq(p2p->op_reg_class, + p2p->op_channel); + if (freq < 0) + freq = 0; + } + p2p->cfg->p2ps_prov_complete( p2p->cfg->cb_ctx, status, sa, adv_mac, p2p->p2ps_prov->session_mac, group_mac, adv_id, p2p->p2ps_prov->session_id, conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, 1, 0, NULL, - msg.feature_cap, msg.feature_cap_len); + msg.feature_cap, msg.feature_cap_len, freq, + msg.group_id ? msg.group_id + ETH_ALEN : NULL, + msg.group_id ? msg.group_id_len - ETH_ALEN : 0); } p2ps_prov_free(p2p); } else if (status != P2P_SC_SUCCESS && @@ -1169,7 +1491,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, p2p->cfg->cb_ctx, status, sa, adv_mac, p2p->p2ps_prov->session_mac, group_mac, adv_id, p2p->p2ps_prov->session_id, - 0, 0, NULL, 0, 1, 0, NULL, NULL, 0); + 0, 0, NULL, 0, 1, 0, NULL, NULL, 0, 0, NULL, 0); p2ps_prov_free(p2p); } @@ -1318,6 +1640,10 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, "Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x", p2p->p2ps_prov->method, p2p->p2ps_prov->status, dev->req_config_methods); + + if (p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq, + p2p->p2ps_prov->pref_freq, 1) < 0) + return -1; } req = p2p_build_prov_disc_req(p2p, dev, join); diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c index 1a2af04b80043..a8bc5ba7f344c 100644 --- a/src/p2p/p2p_sd.c +++ b/src/p2p/p2p_sd.c @@ -28,11 +28,11 @@ static int wfd_wsd_supported(struct wpabuf *wfd) pos = wpabuf_head(wfd); end = pos + wpabuf_len(wfd); - while (pos + 3 <= end) { + while (end - pos >= 3) { subelem = *pos++; len = WPA_GET_BE16(pos); pos += 2; - if (pos + len > end) + if (len > end - pos) break; if (subelem == WFD_SUBELEM_DEVICE_INFO && len >= 6) { @@ -288,6 +288,14 @@ int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev) query = p2p_pending_sd_req(p2p, dev); if (query == NULL) return -1; + if (p2p->state == P2P_SEARCH && + os_memcmp(p2p->sd_query_no_ack, dev->info.p2p_device_addr, + ETH_ALEN) == 0) { + p2p_dbg(p2p, "Do not start Service Discovery with " MACSTR + " due to it being the first no-ACK peer in this search iteration", + MAC2STR(dev->info.p2p_device_addr)); + return -2; + } p2p_dbg(p2p, "Start Service Discovery with " MACSTR, MAC2STR(dev->info.p2p_device_addr)); @@ -355,11 +363,11 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, pos++; slen = *pos++; - next = pos + slen; - if (next > end || slen < 2) { + if (slen > end - pos || slen < 2) { p2p_dbg(p2p, "Invalid IE in GAS Initial Request"); return; } + next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { @@ -370,16 +378,16 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, pos = next; /* Query Request */ - if (pos + 2 > end) + if (end - pos < 2) return; slen = WPA_GET_LE16(pos); pos += 2; - if (pos + slen > end) + if (slen > end - pos) return; end = pos + slen; /* ANQP Query Request */ - if (pos + 4 > end) + if (end - pos < 4) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); @@ -389,7 +397,7 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, slen = WPA_GET_LE16(pos); pos += 2; - if (pos + slen > end || slen < 3 + 1) { + if (slen > end - pos || slen < 3 + 1) { p2p_dbg(p2p, "Invalid ANQP Query Request length"); return; } @@ -401,7 +409,7 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, } pos += 4; - if (pos + 2 > end) + if (end - pos < 2) return; update_indic = WPA_GET_LE16(pos); p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); @@ -417,9 +425,16 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, u8 dialog_token, const struct wpabuf *resp_tlvs) { struct wpabuf *resp; + size_t max_len; + + /* + * In the 60 GHz, we have a smaller maximum frame length for management + * frames. + */ + max_len = (freq > 56160) ? 928 : 1400; /* TODO: fix the length limit to match with the maximum frame length */ - if (wpabuf_len(resp_tlvs) > 1400) { + if (wpabuf_len(resp_tlvs) > max_len) { p2p_dbg(p2p, "SD response long enough to require fragmentation"); if (p2p->sd_resp) { /* @@ -512,11 +527,11 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, pos++; slen = *pos++; - next = pos + slen; - if (next > end || slen < 2) { + if (slen > end - pos || slen < 2) { p2p_dbg(p2p, "Invalid IE in GAS Initial Response"); return; } + next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { @@ -527,14 +542,14 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, pos = next; /* Query Response */ - if (pos + 2 > end) { + if (end - pos < 2) { p2p_dbg(p2p, "Too short Query Response"); return; } slen = WPA_GET_LE16(pos); pos += 2; p2p_dbg(p2p, "Query Response Length: %d", slen); - if (pos + slen > end) { + if (slen > end - pos) { p2p_dbg(p2p, "Not enough Query Response data"); return; } @@ -552,7 +567,7 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, } /* ANQP Query Response */ - if (pos + 4 > end) + if (end - pos < 4) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); @@ -562,7 +577,7 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, slen = WPA_GET_LE16(pos); pos += 2; - if (pos + slen > end || slen < 3 + 1) { + if (slen > end - pos || slen < 3 + 1) { p2p_dbg(p2p, "Invalid ANQP Query Response length"); return; } @@ -574,7 +589,7 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, } pos += 4; - if (pos + 2 > end) + if (end - pos < 2) return; update_indic = WPA_GET_LE16(pos); p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); @@ -606,7 +621,7 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, { struct wpabuf *resp; u8 dialog_token; - size_t frag_len; + size_t frag_len, max_len; int more = 0; wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len); @@ -630,9 +645,14 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, return; } + /* + * In the 60 GHz, we have a smaller maximum frame length for management + * frames. + */ + max_len = (rx_freq > 56160) ? 928 : 1400; frag_len = wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos; - if (frag_len > 1400) { - frag_len = 1400; + if (frag_len > max_len) { + frag_len = max_len; more = 1; } resp = p2p_build_gas_comeback_resp(dialog_token, WLAN_STATUS_SUCCESS, @@ -727,11 +747,11 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, pos++; slen = *pos++; - next = pos + slen; - if (next > end || slen < 2) { + if (slen > end - pos || slen < 2) { p2p_dbg(p2p, "Invalid IE in GAS Comeback Response"); return; } + next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { @@ -742,14 +762,14 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, pos = next; /* Query Response */ - if (pos + 2 > end) { + if (end - pos < 2) { p2p_dbg(p2p, "Too short Query Response"); return; } slen = WPA_GET_LE16(pos); pos += 2; p2p_dbg(p2p, "Query Response Length: %d", slen); - if (pos + slen > end) { + if (slen > end - pos) { p2p_dbg(p2p, "Not enough Query Response data"); return; } @@ -768,7 +788,7 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, } /* ANQP Query Response */ - if (pos + 4 > end) + if (end - pos < 4) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); @@ -783,7 +803,7 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Invalid ANQP Query Response length"); return; } - if (pos + 4 > end) + if (end - pos < 4) return; if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) { @@ -793,7 +813,7 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, } pos += 4; - if (pos + 2 > end) + if (end - pos < 2) return; p2p->sd_rx_update_indic = WPA_GET_LE16(pos); p2p_dbg(p2p, "Service Update Indicator: %u", p2p->sd_rx_update_indic); diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c index cf43c594c4022..e294e64662852 100644 --- a/src/pae/ieee802_1x_cp.c +++ b/src/pae/ieee802_1x_cp.c @@ -20,7 +20,7 @@ #define STATE_MACHINE_DATA struct ieee802_1x_cp_sm #define STATE_MACHINE_DEBUG_PREFIX "CP" -static u8 default_cs_id[] = CS_ID_GCM_AES_128; +static u64 default_cs_id = CS_ID_GCM_AES_128; /* The variable defined in clause 12 in IEEE Std 802.1X-2010 */ enum connect_type { PENDING, UNAUTHENTICATED, AUTHENTICATED, SECURE }; @@ -45,7 +45,7 @@ struct ieee802_1x_cp_sm { Boolean elected_self; u8 *authorization_data1; enum confidentiality_offset cipher_offset; - u8 *cipher_suite; + u64 cipher_suite; Boolean new_sak; /* clear by CP */ struct ieee802_1x_mka_ki distributed_ki; u8 distributed_an; @@ -71,7 +71,7 @@ struct ieee802_1x_cp_sm { Boolean replay_protect; u32 replay_window; - u8 *current_cipher_suite; + u64 current_cipher_suite; enum confidentiality_offset confidentiality_offset; Boolean controlled_port_enabled; @@ -97,8 +97,7 @@ static void ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx, static int changed_cipher(struct ieee802_1x_cp_sm *sm) { return sm->confidentiality_offset != sm->cipher_offset || - os_memcmp(sm->current_cipher_suite, sm->cipher_suite, - CS_ID_LEN) != 0; + sm->current_cipher_suite != sm->cipher_suite; } @@ -185,21 +184,17 @@ SM_STATE(CP, AUTHENTICATED) SM_STATE(CP, SECURED) { - struct ieee802_1x_cp_conf conf; - SM_ENTRY(CP, SECURED); sm->chgd_server = FALSE; - ieee802_1x_kay_cp_conf(sm->kay, &conf); - sm->protect_frames = conf.protect; - sm->replay_protect = conf.replay_protect; - sm->validate_frames = conf.validate; + sm->protect_frames = sm->kay->macsec_protect; + sm->replay_protect = sm->kay->macsec_replay_protect; + sm->validate_frames = sm->kay->macsec_validate; - /* NOTE: now no other than default cipher suiter(AES-GCM-128) */ - os_memcpy(sm->current_cipher_suite, sm->cipher_suite, CS_ID_LEN); - secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite, - CS_ID_LEN); + /* NOTE: now no other than default cipher suite (AES-GCM-128) */ + sm->current_cipher_suite = sm->cipher_suite; + secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite); sm->confidentiality_offset = sm->cipher_offset; @@ -428,9 +423,7 @@ SM_STEP(CP) /** * ieee802_1x_cp_sm_init - */ -struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init( - struct ieee802_1x_kay *kay, - struct ieee802_1x_cp_conf *pcp_conf) +struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay) { struct ieee802_1x_cp_sm *sm; @@ -446,10 +439,10 @@ struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init( sm->chgd_server = FALSE; - sm->protect_frames = pcp_conf->protect; - sm->validate_frames = pcp_conf->validate; - sm->replay_protect = pcp_conf->replay_protect; - sm->replay_window = pcp_conf->replay_window; + sm->protect_frames = kay->macsec_protect; + sm->validate_frames = kay->macsec_validate; + sm->replay_protect = kay->macsec_replay_protect; + sm->replay_window = kay->macsec_replay_window; sm->controlled_port_enabled = FALSE; @@ -460,17 +453,8 @@ struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init( sm->orx = FALSE; sm->otx = FALSE; - sm->cipher_suite = os_zalloc(CS_ID_LEN); - sm->current_cipher_suite = os_zalloc(CS_ID_LEN); - if (!sm->cipher_suite || !sm->current_cipher_suite) { - wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__); - os_free(sm->cipher_suite); - os_free(sm->current_cipher_suite); - os_free(sm); - return NULL; - } - os_memcpy(sm->current_cipher_suite, default_cs_id, CS_ID_LEN); - os_memcpy(sm->cipher_suite, default_cs_id, CS_ID_LEN); + sm->current_cipher_suite = default_cs_id; + sm->cipher_suite = default_cs_id; sm->cipher_offset = CONFIDENTIALITY_OFFSET_0; sm->confidentiality_offset = sm->cipher_offset; sm->transmit_delay = MKA_LIFE_TIME; @@ -530,8 +514,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->cipher_suite); - os_free(sm->current_cipher_suite); os_free(sm->authorization_data); os_free(sm); } @@ -618,10 +600,10 @@ void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len) /** * ieee802_1x_cp_set_ciphersuite - */ -void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, void *pid) +void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, u64 cs) { struct ieee802_1x_cp_sm *sm = cp_ctx; - os_memcpy(sm->cipher_suite, pid, CS_ID_LEN); + sm->cipher_suite = cs; } diff --git a/src/pae/ieee802_1x_cp.h b/src/pae/ieee802_1x_cp.h index 773c93052bf65..695629e5c0bce 100644 --- a/src/pae/ieee802_1x_cp.h +++ b/src/pae/ieee802_1x_cp.h @@ -16,17 +16,7 @@ struct ieee802_1x_cp_sm; struct ieee802_1x_kay; struct ieee802_1x_mka_ki; -struct ieee802_1x_cp_conf { - Boolean protect; - Boolean replay_protect; - enum validate_frames validate; - u32 replay_window; -}; - - -struct ieee802_1x_cp_sm * -ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay, - struct ieee802_1x_cp_conf *pcp_conf); +struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay); void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm); void ieee802_1x_cp_sm_step(void *cp_ctx); void ieee802_1x_cp_connect_pending(void *cp_ctx); @@ -36,7 +26,7 @@ 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, void *pid); +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); void ieee802_1x_cp_set_distributedki(void *cp_ctx, diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c index ef744304a2bbf..a8e7efc9b3bda 100644 --- a/src/pae/ieee802_1x_kay.c +++ b/src/pae/ieee802_1x_kay.c @@ -29,6 +29,8 @@ #define PENDING_PN_EXHAUSTION 0xC0000000 +#define MKA_ALIGN_LENGTH(len) (((len) + 0x3) & ~0x3) + /* IEEE Std 802.1X-2010, Table 9-1 - MKA Algorithm Agility */ #define MKA_ALGO_AGILITY_2009 { 0x00, 0x80, 0xC2, 0x01 } static u8 mka_algo_agility[4] = MKA_ALGO_AGILITY_2009; @@ -37,12 +39,11 @@ static u8 mka_algo_agility[4] = MKA_ALGO_AGILITY_2009; static struct macsec_ciphersuite cipher_suite_tbl[] = { /* GCM-AES-128 */ { - CS_ID_GCM_AES_128, - CS_NAME_GCM_AES_128, - MACSEC_CAP_INTEG_AND_CONF_0_30_50, - 16, - - 0 /* index */ + .id = CS_ID_GCM_AES_128, + .name = CS_NAME_GCM_AES_128, + .capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50, + .sak_len = DEFAULT_SA_KEY_LEN, + .index = 0, }, }; #define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl)) @@ -50,16 +51,21 @@ static struct macsec_ciphersuite cipher_suite_tbl[] = { static struct mka_alg mka_alg_tbl[] = { { - MKA_ALGO_AGILITY_2009, + .parameter = MKA_ALGO_AGILITY_2009, + /* 128-bit CAK, KEK, ICK, ICV */ - 16, 16, 16, 16, - ieee802_1x_cak_128bits_aes_cmac, - ieee802_1x_ckn_128bits_aes_cmac, - ieee802_1x_kek_128bits_aes_cmac, - ieee802_1x_ick_128bits_aes_cmac, - ieee802_1x_icv_128bits_aes_cmac, - - 1, /* index */ + .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, }, }; #define MKA_ALG_TABLE_SIZE (ARRAY_SIZE(mka_alg_tbl)) @@ -73,16 +79,6 @@ static int is_ki_equal(struct ieee802_1x_mka_ki *ki1, } -struct mka_param_body_handler { - int (*body_tx)(struct ieee802_1x_mka_participant *participant, - struct wpabuf *buf); - int (*body_rx)(struct ieee802_1x_mka_participant *participant, - const u8 *mka_msg, size_t msg_len); - int (*body_length)(struct ieee802_1x_mka_participant *participant); - Boolean (*body_present)(struct ieee802_1x_mka_participant *participant); -}; - - static void set_mka_param_body_len(void *body, unsigned int len) { struct ieee802_1x_mka_hdr *hdr = body; @@ -98,7 +94,7 @@ static unsigned int get_mka_param_body_len(const void *body) } -static int get_mka_param_body_type(const void *body) +static u8 get_mka_param_body_type(const void *body) { const struct ieee802_1x_mka_hdr *hdr = body; return hdr->type; @@ -122,8 +118,8 @@ ieee802_1x_mka_dump_basic_body(struct ieee802_1x_mka_basic_body *body) 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_capbility); - wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len); + 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", @@ -148,7 +144,7 @@ ieee802_1x_mka_dump_peer_body(struct ieee802_1x_mka_peer_body *body) size_t body_len; size_t i; u8 *mi; - u32 mn; + be32 mn; if (body == NULL) return; @@ -156,10 +152,10 @@ ieee802_1x_mka_dump_peer_body(struct ieee802_1x_mka_peer_body *body) 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...: %d", (int) body_len); + 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...: %d", (int) body_len); + wpa_printf(MSG_DEBUG, "\tBody Length...: %zu", body_len); } for (i = 0; i < body_len; i += MI_LEN + sizeof(mn)) { @@ -187,7 +183,7 @@ ieee802_1x_mka_dump_dist_sak_body(struct ieee802_1x_mka_dist_sak_body *body) wpa_printf(MSG_INFO, "\tDistributed AN........: %d", body->dan); wpa_printf(MSG_INFO, "\tConfidentiality Offset: %d", body->confid_offset); - wpa_printf(MSG_INFO, "\tBody Length...........: %d", (int) body_len); + wpa_printf(MSG_INFO, "\tBody Length...........: %zu", body_len); if (!body_len) return; @@ -280,7 +276,7 @@ ieee802_1x_kay_get_principal_participant(struct ieee802_1x_kay *kay) return participant; } - wpa_printf(MSG_DEBUG, "KaY: principal participant is not founded"); + wpa_printf(MSG_DEBUG, "KaY: principal participant is not found"); return NULL; } @@ -300,36 +296,46 @@ static struct ieee802_1x_kay_peer * get_peer_mi(struct dl_list *peers, /** - * ieee802_1x_kay_is_in_potential_peer + * ieee802_1x_kay_get_potential_peer */ -static Boolean -ieee802_1x_kay_is_in_potential_peer( +static struct ieee802_1x_kay_peer * +ieee802_1x_kay_get_potential_peer( struct ieee802_1x_mka_participant *participant, const u8 *mi) { - return get_peer_mi(&participant->potential_peers, mi) != NULL; + return get_peer_mi(&participant->potential_peers, mi); } /** - * ieee802_1x_kay_is_in_live_peer + * ieee802_1x_kay_get_live_peer + */ +static struct ieee802_1x_kay_peer * +ieee802_1x_kay_get_live_peer(struct ieee802_1x_mka_participant *participant, + const u8 *mi) +{ + return get_peer_mi(&participant->live_peers, mi); +} + + +/** + * ieee802_1x_kay_is_in_potential_peer */ static Boolean -ieee802_1x_kay_is_in_live_peer( +ieee802_1x_kay_is_in_potential_peer( struct ieee802_1x_mka_participant *participant, const u8 *mi) { - return get_peer_mi(&participant->live_peers, mi) != NULL; + return ieee802_1x_kay_get_potential_peer(participant, mi) != NULL; } /** - * ieee802_1x_kay_is_in_peer + * ieee802_1x_kay_is_in_live_peer */ static Boolean -ieee802_1x_kay_is_in_peer(struct ieee802_1x_mka_participant *participant, - const u8 *mi) +ieee802_1x_kay_is_in_live_peer( + struct ieee802_1x_mka_participant *participant, const u8 *mi) { - return ieee802_1x_kay_is_in_live_peer(participant, mi) || - ieee802_1x_kay_is_in_potential_peer(participant, mi); + return ieee802_1x_kay_get_live_peer(participant, mi) != NULL; } @@ -342,22 +348,11 @@ ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant, { struct ieee802_1x_kay_peer *peer; - peer = get_peer_mi(&participant->live_peers, mi); + peer = ieee802_1x_kay_get_live_peer(participant, mi); if (peer) return peer; - return get_peer_mi(&participant->potential_peers, mi); -} - - -/** - * ieee802_1x_kay_get_live_peer - */ -static struct ieee802_1x_kay_peer * -ieee802_1x_kay_get_live_peer(struct ieee802_1x_mka_participant *participant, - const u8 *mi) -{ - return get_peer_mi(&participant->live_peers, mi); + return ieee802_1x_kay_get_potential_peer(participant, mi); } @@ -366,18 +361,28 @@ ieee802_1x_kay_get_live_peer(struct ieee802_1x_mka_participant *participant, */ static struct macsec_ciphersuite * ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant, - u8 *cs_id) + const u8 *cs_id) { unsigned int i; + u64 cs; + be64 _cs; + + os_memcpy(&_cs, cs_id, CS_ID_LEN); + cs = be_to_host64(_cs); for (i = 0; i < CS_TABLE_SIZE; i++) { - if (os_memcmp(cipher_suite_tbl[i].id, cs_id, CS_ID_LEN) == 0) - break; + if (cipher_suite_tbl[i].id == cs) + return &cipher_suite_tbl[i]; } - if (i >= CS_TABLE_SIZE) - return NULL; - return &cipher_suite_tbl[i]; + return NULL; +} + + +static Boolean sci_equal(const struct ieee802_1x_mka_sci *a, + const struct ieee802_1x_mka_sci *b) +{ + return os_memcmp(a, b, sizeof(struct ieee802_1x_mka_sci)) == 0; } @@ -392,13 +397,13 @@ ieee802_1x_kay_get_peer_sci(struct ieee802_1x_mka_participant *participant, dl_list_for_each(peer, &participant->live_peers, struct ieee802_1x_kay_peer, list) { - if (os_memcmp(&peer->sci, sci, sizeof(peer->sci)) == 0) + if (sci_equal(&peer->sci, sci)) return peer; } dl_list_for_each(peer, &participant->potential_peers, struct ieee802_1x_kay_peer, list) { - if (os_memcmp(&peer->sci, sci, sizeof(peer->sci)) == 0) + if (sci_equal(&peer->sci, sci)) return peer; } @@ -435,8 +440,8 @@ 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: %d lowest_pn: %u of SC(channel: %d)", - (int) an, lowest_pn, psc->channel); + "KaY: Create receive SA(AN: %hhu lowest_pn: %u of SC(channel: %d)", + an, lowest_pn, psc->channel); return psa; } @@ -449,8 +454,8 @@ static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa) { psa->pkey = NULL; wpa_printf(MSG_DEBUG, - "KaY: Delete receive SA(an: %d) of SC(channel: %d)", - psa->an, psa->sc->channel); + "KaY: Delete receive SA(an: %hhu) of SC", + psa->an); dl_list_del(&psa->list); os_free(psa); } @@ -509,19 +514,22 @@ ieee802_1x_kay_deinit_receive_sc( } -/** - * ieee802_1x_kay_create_live_peer - */ +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); +} + + static struct ieee802_1x_kay_peer * -ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant, - u8 *mi, u32 mn) +ieee802_1x_kay_create_peer(const u8 *mi, u32 mn) { struct ieee802_1x_kay_peer *peer; - struct receive_sc *rxsc; - u32 sc_ch = 0; peer = os_zalloc(sizeof(*peer)); - if (peer == NULL) { + if (!peer) { wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); return NULL; } @@ -530,24 +538,43 @@ ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant, peer->mn = mn; peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; peer->sak_used = FALSE; + + return peer; +} + + +/** + * ieee802_1x_kay_create_live_peer + */ +static struct ieee802_1x_kay_peer * +ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant, + const u8 *mi, u32 mn) +{ + struct ieee802_1x_kay_peer *peer; + struct receive_sc *rxsc; + u32 sc_ch = 0; + + peer = ieee802_1x_kay_create_peer(mi, mn); + if (!peer) + return NULL; + os_memcpy(&peer->sci, &participant->current_peer_sci, sizeof(peer->sci)); - dl_list_add(&participant->live_peers, &peer->list); secy_get_available_receive_sc(participant->kay, &sc_ch); rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch); - if (!rxsc) + if (!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"); - 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); + ieee802_1x_kay_dump_peer(peer); return peer; } @@ -562,24 +589,14 @@ ieee802_1x_kay_create_potential_peer( { struct ieee802_1x_kay_peer *peer; - peer = os_zalloc(sizeof(*peer)); - if (peer == NULL) { - wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); + peer = ieee802_1x_kay_create_peer(mi, mn); + if (!peer) return NULL; - } - - os_memcpy(peer->mi, mi, MI_LEN); - peer->mn = mn; - peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; - peer->sak_used = FALSE; dl_list_add(&participant->potential_peers, &peer->list); wpa_printf(MSG_DEBUG, "KaY: potential peer created"); - 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); + ieee802_1x_kay_dump_peer(peer); return peer; } @@ -596,11 +613,12 @@ ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant, struct receive_sc *rxsc; u32 sc_ch = 0; - dl_list_for_each(peer, &participant->potential_peers, - struct ieee802_1x_kay_peer, list) { - if (os_memcmp(peer->mi, mi, MI_LEN) == 0) - break; - } + peer = ieee802_1x_kay_get_potential_peer(participant, mi); + + rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci, + sc_ch); + if (!rxsc) + return NULL; os_memcpy(&peer->sci, &participant->current_peer_sci, sizeof(peer->sci)); @@ -608,20 +626,13 @@ ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant, peer->expire = time(NULL) + MKA_LIFE_TIME / 1000; wpa_printf(MSG_DEBUG, "KaY: move potential peer to live 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); + ieee802_1x_kay_dump_peer(peer); dl_list_del(&peer->list); dl_list_add_tail(&participant->live_peers, &peer->list); secy_get_available_receive_sc(participant->kay, &sc_ch); - rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch); - if (!rxsc) - return NULL; - dl_list_add(&participant->rxsc_list, &rxsc->list); secy_create_receive_sc(participant->kay, rxsc); @@ -651,7 +662,7 @@ ieee802_1x_mka_basic_body_length(struct ieee802_1x_mka_participant *participant) length = sizeof(struct ieee802_1x_mka_basic_body); length += participant->ckn.len; - return (length + 0x3) & ~0x3; + return MKA_ALIGN_LENGTH(length); } @@ -677,17 +688,17 @@ ieee802_1x_mka_encode_basic_body( body->key_server = participant->can_be_key_server; body->macsec_desired = kay->macsec_desired; - body->macsec_capbility = kay->macsec_capable; + body->macsec_capability = kay->macsec_capable; set_mka_param_body_len(body, length - MKA_HDR_LEN); os_memcpy(body->actor_sci.addr, kay->actor_sci.addr, sizeof(kay->actor_sci.addr)); - body->actor_sci.port = host_to_be16(kay->actor_sci.port); + body->actor_sci.port = kay->actor_sci.port; os_memcpy(body->actor_mi, participant->mi, sizeof(body->actor_mi)); participant->mn = participant->mn + 1; body->actor_mn = host_to_be32(participant->mn); - os_memcpy(body->algo_agility, participant->kay->algo_agility, + os_memcpy(body->algo_agility, kay->algo_agility, sizeof(body->algo_agility)); os_memcpy(body->ckn, participant->ckn.name, participant->ckn.len); @@ -698,6 +709,17 @@ ieee802_1x_mka_encode_basic_body( } +static Boolean +reset_participant_mi(struct ieee802_1x_mka_participant *participant) +{ + if (os_get_random(participant->mi, sizeof(participant->mi)) < 0) + return FALSE; + participant->mn = 0; + + return TRUE; +} + + /** * ieee802_1x_mka_decode_basic_body - */ @@ -729,16 +751,15 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, /* If the peer's MI is my MI, I will choose new MI */ if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) { - if (os_get_random(participant->mi, sizeof(participant->mi)) < 0) + if (!reset_participant_mi(participant)) return NULL; - participant->mn = 0; } os_memcpy(participant->current_peer_id.mi, body->actor_mi, MI_LEN); - participant->current_peer_id.mn = be_to_host32(body->actor_mn); + participant->current_peer_id.mn = body->actor_mn; os_memcpy(participant->current_peer_sci.addr, body->actor_sci.addr, sizeof(participant->current_peer_sci.addr)); - participant->current_peer_sci.port = be_to_host16(body->actor_sci.port); + participant->current_peer_sci.port = body->actor_sci.port; /* handler peer */ peer = ieee802_1x_kay_get_peer(participant, body->actor_mi); @@ -763,14 +784,14 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, return NULL; peer->macsec_desired = body->macsec_desired; - peer->macsec_capbility = body->macsec_capbility; + peer->macsec_capability = body->macsec_capability; peer->is_key_server = (Boolean) body->key_server; 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_capbility = body->macsec_capbility; + peer->macsec_capability = body->macsec_capability; peer->is_key_server = (Boolean) body->key_server; peer->key_server_priority = body->priority; } else { @@ -807,7 +828,7 @@ ieee802_1x_mka_get_live_peer_length( struct ieee802_1x_kay_peer, list) len += sizeof(struct ieee802_1x_mka_peer_id); - return (len + 0x3) & ~0x3; + return MKA_ALIGN_LENGTH(len); } @@ -836,7 +857,6 @@ ieee802_1x_mka_encode_live_peer_body( sizeof(struct ieee802_1x_mka_peer_id)); os_memcpy(body_peer->mi, peer->mi, MI_LEN); body_peer->mn = host_to_be32(peer->mn); - body_peer++; } ieee802_1x_mka_dump_peer_body(body); @@ -868,7 +888,7 @@ ieee802_1x_mka_get_potential_peer_length( struct ieee802_1x_kay_peer, list) len += sizeof(struct ieee802_1x_mka_peer_id); - return (len + 0x3) & ~0x3; + return MKA_ALIGN_LENGTH(len); } @@ -897,7 +917,6 @@ ieee802_1x_mka_encode_potential_peer_body( sizeof(struct ieee802_1x_mka_peer_id)); os_memcpy(body_peer->mi, peer->mi, MI_LEN); body_peer->mn = host_to_be32(peer->mn); - body_peer++; } ieee802_1x_mka_dump_peer_body(body); @@ -912,62 +931,54 @@ static Boolean ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant, const u8 *mka_msg, size_t msg_len) { - Boolean included = FALSE; struct ieee802_1x_mka_hdr *hdr; size_t body_len; size_t left_len; - int body_type; - u32 peer_mn; - const u8 *peer_mi; + u8 body_type; const u8 *pos; size_t i; - pos = mka_msg; - left_len = msg_len; - while (left_len > (MKA_HDR_LEN + DEFAULT_ICV_LEN)) { + 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) { hdr = (struct ieee802_1x_mka_hdr *) pos; body_len = get_mka_param_body_len(hdr); body_type = get_mka_param_body_type(hdr); if (body_type != MKA_LIVE_PEER_LIST && body_type != MKA_POTENTIAL_PEER_LIST) - goto SKIP_PEER; + continue; ieee802_1x_mka_dump_peer_body( (struct ieee802_1x_mka_peer_body *)pos); if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) { wpa_printf(MSG_ERROR, - "KaY: MKA Peer Packet Body Length (%d bytes) is less than the Parameter Set Header Length (%d bytes) + the Parameter Set Body Length (%d bytes) + %d bytes of ICV", - (int) left_len, (int) MKA_HDR_LEN, - (int) body_len, DEFAULT_ICV_LEN); - goto SKIP_PEER; + "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV", + left_len, MKA_HDR_LEN, + body_len, DEFAULT_ICV_LEN); + continue; } if ((body_len % 16) != 0) { wpa_printf(MSG_ERROR, - "KaY: MKA Peer Packet Body Length (%d bytes) should multiple of 16 octets", - (int) body_len); - goto SKIP_PEER; - } - - for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) { - peer_mi = MKA_HDR_LEN + pos + i; - os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn)); - peer_mn = be_to_host32(peer_mn); - if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0 && - peer_mn == participant->mn) { - included = TRUE; - break; - } + "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets", + body_len); + continue; } - if (included) - return TRUE; + for (i = 0; i < body_len; + i += sizeof(struct ieee802_1x_mka_peer_id)) { + const struct ieee802_1x_mka_peer_id *peer_mi; -SKIP_PEER: - left_len -= body_len + MKA_HDR_LEN; - pos += body_len + MKA_HDR_LEN; + 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; + } } return FALSE; @@ -984,8 +995,6 @@ static int ieee802_1x_mka_decode_live_peer_body( const struct ieee802_1x_mka_hdr *hdr; struct ieee802_1x_kay_peer *peer; size_t body_len; - u32 peer_mn; - const u8 *peer_mi; size_t i; Boolean is_included; @@ -994,36 +1003,40 @@ static int ieee802_1x_mka_decode_live_peer_body( hdr = (const struct ieee802_1x_mka_hdr *) peer_msg; body_len = get_mka_param_body_len(hdr); + if (body_len % 16 != 0) { + wpa_printf(MSG_ERROR, + "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets", + body_len); + return -1; + } + + for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) { + const struct ieee802_1x_mka_peer_id *peer_mi; + u32 peer_mn; - for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) { - peer_mi = MKA_HDR_LEN + peer_msg + i; - os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn)); - peer_mn = be_to_host32(peer_mn); + peer_mi = (const struct ieee802_1x_mka_peer_id *) + (peer_msg + MKA_HDR_LEN + i); + peer_mn = be_to_host32(peer_mi->mn); /* it is myself */ if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) { /* My message id is used by other participant */ - if (peer_mn > participant->mn) { - if (os_get_random(participant->mi, - sizeof(participant->mi)) < 0) - wpa_printf(MSG_DEBUG, - "KaY: Could not update mi"); - participant->mn = 0; - } + if (peer_mn > participant->mn && + !reset_participant_mi(participant)) + wpa_printf(MSG_DEBUG, "KaY: Could not update mi"); continue; } + if (!is_included) continue; - peer = ieee802_1x_kay_get_peer(participant, peer_mi); - if (NULL != peer) { + 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, peer_mn)) { - return -1; - } + } else if (!ieee802_1x_kay_create_potential_peer( + participant, peer_mi->mi, peer_mn)) { + return -1; } } @@ -1039,30 +1052,33 @@ ieee802_1x_mka_decode_potential_peer_body( struct ieee802_1x_mka_participant *participant, const u8 *peer_msg, size_t msg_len) { - struct ieee802_1x_mka_hdr *hdr; + const struct ieee802_1x_mka_hdr *hdr; size_t body_len; - u32 peer_mn; - const u8 *peer_mi; size_t i; - hdr = (struct ieee802_1x_mka_hdr *) peer_msg; + hdr = (const struct ieee802_1x_mka_hdr *) peer_msg; body_len = get_mka_param_body_len(hdr); + if (body_len % 16 != 0) { + wpa_printf(MSG_ERROR, + "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets", + body_len); + return -1; + } - for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) { - peer_mi = MKA_HDR_LEN + peer_msg + i; - os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn)); - peer_mn = be_to_host32(peer_mn); + for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) { + const struct ieee802_1x_mka_peer_id *peer_mi; + u32 peer_mn; + + peer_mi = (struct ieee802_1x_mka_peer_id *) + (peer_msg + MKA_HDR_LEN + i); + peer_mn = be_to_host32(peer_mi->mn); /* it is myself */ if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) { /* My message id is used by other participant */ - if (peer_mn > participant->mn) { - if (os_get_random(participant->mi, - sizeof(participant->mi)) < 0) - wpa_printf(MSG_DEBUG, - "KaY: Could not update mi"); - participant->mn = 0; - } + if (peer_mn > participant->mn && + !reset_participant_mi(participant)) + wpa_printf(MSG_DEBUG, "KaY: Could not update mi"); continue; } } @@ -1078,10 +1094,7 @@ static Boolean ieee802_1x_mka_sak_use_body_present( struct ieee802_1x_mka_participant *participant) { - if (participant->to_use_sak) - return TRUE; - else - return FALSE; + return participant->to_use_sak; } @@ -1096,12 +1109,8 @@ ieee802_1x_mka_get_sak_use_length( if (participant->kay->macsec_desired && participant->advised_desired) length = sizeof(struct ieee802_1x_mka_sak_use_body); - else - length = MKA_HDR_LEN; - length = (length + 0x3) & ~0x3; - - return length; + return MKA_ALIGN_LENGTH(length); } @@ -1146,11 +1155,12 @@ ieee802_1x_mka_encode_sak_use_body( struct wpabuf *buf) { struct ieee802_1x_mka_sak_use_body *body; + struct ieee802_1x_kay *kay = participant->kay; unsigned int length; u32 pn = 1; length = ieee802_1x_mka_get_sak_use_length(participant); - body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_sak_use_body)); + body = wpabuf_put(buf, length); body->type = MKA_SAK_USE; set_mka_param_body_len(body, length - MKA_HDR_LEN); @@ -1166,9 +1176,9 @@ ieee802_1x_mka_encode_sak_use_body( } /* data protect, lowest accept packet number */ - body->delay_protect = participant->kay->macsec_replay_protect; + body->delay_protect = kay->macsec_replay_protect; pn = ieee802_1x_mka_get_lpn(participant, &participant->lki); - if (pn > participant->kay->pn_exhaustion) { + if (pn > kay->pn_exhaustion) { wpa_printf(MSG_WARNING, "KaY: My LPN exhaustion"); if (participant->is_key_server) participant->new_sak = TRUE; @@ -1179,20 +1189,12 @@ ieee802_1x_mka_encode_sak_use_body( body->olpn = host_to_be32(pn); /* plain tx, plain rx */ - if (participant->kay->macsec_protect) - body->ptx = FALSE; - else - body->ptx = TRUE; - - if (participant->kay->macsec_validate == Strict) - body->prx = FALSE; - else - body->prx = TRUE; + body->ptx = !kay->macsec_protect; + body->prx = kay->macsec_validate != Strict; /* latest key: rx, tx, key server member identifier key number */ body->lan = participant->lan; - os_memcpy(body->lsrv_mi, participant->lki.mi, - sizeof(body->lsrv_mi)); + os_memcpy(body->lsrv_mi, participant->lki.mi, sizeof(body->lsrv_mi)); body->lkn = host_to_be32(participant->lki.kn); body->lrx = participant->lrx; body->ltx = participant->ltx; @@ -1213,16 +1215,11 @@ ieee802_1x_mka_encode_sak_use_body( /* set CP's variable */ if (body->ltx) { - if (!participant->kay->tx_enable) - participant->kay->tx_enable = TRUE; - - if (!participant->kay->port_enable) - participant->kay->port_enable = TRUE; - } - if (body->lrx) { - if (!participant->kay->rx_enable) - participant->kay->rx_enable = TRUE; + kay->tx_enable = TRUE; + kay->port_enable = TRUE; } + if (body->lrx) + kay->rx_enable = TRUE; ieee802_1x_mka_dump_sak_use_body(body); return 0; @@ -1246,7 +1243,8 @@ ieee802_1x_mka_decode_sak_use_body( struct ieee802_1x_mka_ki ki; u32 lpn; Boolean all_receiving; - Boolean founded; + Boolean found; + struct ieee802_1x_kay *kay = participant->kay; if (!participant->principal) { wpa_printf(MSG_WARNING, "KaY: Participant is not principal"); @@ -1266,8 +1264,8 @@ ieee802_1x_mka_decode_sak_use_body( if ((body_len != 0) && (body_len < 40)) { wpa_printf(MSG_ERROR, - "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 0, 40, or more octets", - (int) body_len); + "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 40, or more octets", + body_len); return -1; } @@ -1288,30 +1286,29 @@ ieee802_1x_mka_decode_sak_use_body( /* check latest key is valid */ if (body->ltx || body->lrx) { - founded = FALSE; + found = FALSE; os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi)); - ki.kn = ntohl(body->lkn); + ki.kn = be_to_host32(body->lkn); dl_list_for_each(sa_key, &participant->sak_list, struct data_key, list) { if (is_ki_equal(&sa_key->key_identifier, &ki)) { - founded = TRUE; + found = TRUE; break; } } - if (!founded) { + if (!found) { wpa_printf(MSG_WARNING, "KaY: Latest key is invalid"); return -1; } if (os_memcmp(participant->lki.mi, body->lsrv_mi, sizeof(participant->lki.mi)) == 0 && - ntohl(body->lkn) == participant->lki.kn && + be_to_host32(body->lkn) == participant->lki.kn && body->lan == participant->lan) { peer->sak_used = TRUE; } if (body->ltx && peer->is_key_server) { - ieee802_1x_cp_set_servertransmitting( - participant->kay->cp, TRUE); - ieee802_1x_cp_sm_step(participant->kay->cp); + ieee802_1x_cp_set_servertransmitting(kay->cp, TRUE); + ieee802_1x_cp_sm_step(kay->cp); } } @@ -1319,7 +1316,7 @@ ieee802_1x_mka_decode_sak_use_body( if (body->otx || body->orx) { if (os_memcmp(participant->oki.mi, body->osrv_mi, sizeof(participant->oki.mi)) != 0 || - ntohl(body->okn) != participant->oki.kn || + be_to_host32(body->okn) != participant->oki.kn || body->oan != participant->oan) { wpa_printf(MSG_WARNING, "KaY: Old key is invalid"); return -1; @@ -1327,7 +1324,8 @@ ieee802_1x_mka_decode_sak_use_body( } /* TODO: how to set the MACsec hardware when delay_protect is true */ - if (body->delay_protect && (!ntohl(body->llpn) || !ntohl(body->olpn))) { + 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"); return -1; @@ -1344,28 +1342,28 @@ ieee802_1x_mka_decode_sak_use_body( } if (all_receiving) { participant->to_dist_sak = FALSE; - ieee802_1x_cp_set_allreceiving(participant->kay->cp, TRUE); - ieee802_1x_cp_sm_step(participant->kay->cp); + ieee802_1x_cp_set_allreceiving(kay->cp, TRUE); + ieee802_1x_cp_sm_step(kay->cp); } /* if i'm key server, and detects peer member pn exhaustion, rekey.*/ - lpn = ntohl(body->llpn); - if (lpn > participant->kay->pn_exhaustion) { + lpn = be_to_host32(body->llpn); + if (lpn > kay->pn_exhaustion) { if (participant->is_key_server) { participant->new_sak = TRUE; wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion"); } } - founded = FALSE; + found = FALSE; dl_list_for_each(txsa, &participant->txsc->sa_list, struct transmit_sa, list) { if (sa_key != NULL && txsa->pkey == sa_key) { - founded = TRUE; + found = TRUE; break; } } - if (!founded) { + if (!found) { wpa_printf(MSG_WARNING, "KaY: Can't find txsa"); return -1; } @@ -1373,9 +1371,9 @@ ieee802_1x_mka_decode_sak_use_body( /* 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(participant->kay, txsa); + secy_get_transmit_next_pn(kay, txsa); if (lpn > txsa->next_pn) { - secy_set_transmit_next_pn(participant->kay, txsa); + secy_set_transmit_next_pn(kay, txsa); wpa_printf(MSG_INFO, "KaY: update lpn =0x%x", lpn); } @@ -1390,10 +1388,7 @@ static Boolean ieee802_1x_mka_dist_sak_body_present( struct ieee802_1x_mka_participant *participant) { - if (!participant->to_dist_sak || !participant->new_key) - return FALSE; - - return TRUE; + return participant->to_dist_sak && participant->new_key; } @@ -1404,21 +1399,18 @@ static int ieee802_1x_mka_get_dist_sak_length( struct ieee802_1x_mka_participant *participant) { - int length; - int cs_index = participant->kay->macsec_csindex; + int length = MKA_HDR_LEN; + unsigned int cs_index = participant->kay->macsec_csindex; - if (participant->advised_desired) { + if (participant->advised_desired && cs_index < CS_TABLE_SIZE) { length = sizeof(struct ieee802_1x_mka_dist_sak_body); if (cs_index != DEFAULT_CS_INDEX) length += CS_ID_LEN; length += cipher_suite_tbl[cs_index].sak_len + 8; - } else { - length = MKA_HDR_LEN; } - length = (length + 0x3) & ~0x3; - return length; + return MKA_ALIGN_LENGTH(length); } @@ -1433,7 +1425,7 @@ ieee802_1x_mka_encode_dist_sak_body( struct ieee802_1x_mka_dist_sak_body *body; struct data_key *sak; unsigned int length; - int cs_index; + unsigned int cs_index; int sak_pos; length = ieee802_1x_mka_get_dist_sak_length(participant); @@ -1452,8 +1444,13 @@ ieee802_1x_mka_encode_dist_sak_body( body->kn = host_to_be32(sak->key_identifier.kn); cs_index = participant->kay->macsec_csindex; sak_pos = 0; + if (cs_index >= CS_TABLE_SIZE) + return -1; if (cs_index != DEFAULT_CS_INDEX) { - os_memcpy(body->sak, cipher_suite_tbl[cs_index].id, CS_ID_LEN); + be64 cs; + + cs = host_to_be64(cipher_suite_tbl[cs_index].id); + os_memcpy(body->sak, &cs, CS_ID_LEN); sak_pos = CS_ID_LEN; } if (aes_wrap(participant->kek.key, 16, @@ -1472,39 +1469,13 @@ ieee802_1x_mka_encode_dist_sak_body( /** * ieee802_1x_kay_init_data_key - */ -static struct data_key * -ieee802_1x_kay_init_data_key(const struct key_conf *conf) +static void ieee802_1x_kay_init_data_key(struct data_key *pkey) { - struct data_key *pkey; - - if (!conf) - return NULL; - - pkey = os_zalloc(sizeof(*pkey)); - if (pkey == NULL) { - wpa_printf(MSG_ERROR, "%s: out of memory", __func__); - return NULL; - } - - pkey->key = os_zalloc(conf->key_len); - if (pkey->key == NULL) { - wpa_printf(MSG_ERROR, "%s: out of memory", __func__); - os_free(pkey); - return NULL; - } - - os_memcpy(pkey->key, conf->key, conf->key_len); - os_memcpy(&pkey->key_identifier, &conf->ki, - sizeof(pkey->key_identifier)); - pkey->confidentiality_offset = conf->offset; - pkey->an = conf->an; - pkey->transmits = conf->tx; - pkey->receives = conf->rx; + pkey->transmits = TRUE; + pkey->receives = TRUE; os_get_time(&pkey->created_time); pkey->user = 1; - - return pkey; } @@ -1521,19 +1492,18 @@ ieee802_1x_mka_decode_dist_sak_body( struct ieee802_1x_kay_peer *peer; struct macsec_ciphersuite *cs; size_t body_len; - struct key_conf *conf; struct data_key *sa_key = NULL; - struct ieee802_1x_mka_ki sak_ki; int sak_len; u8 *wrap_sak; u8 *unwrap_sak; + struct ieee802_1x_kay *kay = participant->kay; hdr = (struct ieee802_1x_mka_hdr *) mka_msg; body_len = get_mka_param_body_len(hdr); if ((body_len != 0) && (body_len != 28) && (body_len < 36)) { wpa_printf(MSG_ERROR, - "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 0, 28, 36, or more octets", - (int) body_len); + "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 28, 36, or more octets", + body_len); return -1; } @@ -1547,8 +1517,8 @@ ieee802_1x_mka_decode_dist_sak_body( "KaY: I can't accept the distributed SAK as myself is key server "); return -1; } - if (!participant->kay->macsec_desired || - participant->kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) { + if (!kay->macsec_desired || + kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) { wpa_printf(MSG_ERROR, "KaY: I am not MACsec-desired or without MACsec capable"); return -1; @@ -1561,28 +1531,29 @@ ieee802_1x_mka_decode_dist_sak_body( "KaY: The key server is not in my live peers list"); return -1; } - if (os_memcmp(&participant->kay->key_server_sci, - &peer->sci, sizeof(struct ieee802_1x_mka_sci)) != 0) { + if (!sci_equal(&kay->key_server_sci, &peer->sci)) { wpa_printf(MSG_ERROR, "KaY: The key server is not elected"); return -1; } + if (body_len == 0) { - participant->kay->authenticated = TRUE; - participant->kay->secured = FALSE; - participant->kay->failed = FALSE; + kay->authenticated = TRUE; + kay->secured = FALSE; + kay->failed = FALSE; participant->advised_desired = FALSE; - ieee802_1x_cp_connect_authenticated(participant->kay->cp); - ieee802_1x_cp_sm_step(participant->kay->cp); + ieee802_1x_cp_connect_authenticated(kay->cp); + ieee802_1x_cp_sm_step(kay->cp); wpa_printf(MSG_WARNING, "KaY:The Key server advise no MACsec"); participant->to_use_sak = TRUE; return 0; } + participant->advised_desired = TRUE; - participant->kay->authenticated = FALSE; - participant->kay->secured = TRUE; - participant->kay->failed = FALSE; - ieee802_1x_cp_connect_secure(participant->kay->cp); - ieee802_1x_cp_sm_step(participant->kay->cp); + kay->authenticated = FALSE; + kay->secured = TRUE; + kay->failed = FALSE; + ieee802_1x_cp_connect_secure(kay->cp); + ieee802_1x_cp_sm_step(kay->cp); body = (struct ieee802_1x_mka_dist_sak_body *)mka_msg; ieee802_1x_mka_dump_dist_sak_body(body); @@ -1595,10 +1566,12 @@ ieee802_1x_mka_decode_dist_sak_body( return 0; } } + if (body_len == 28) { sak_len = DEFAULT_SA_KEY_LEN; wrap_sak = body->sak; - participant->kay->macsec_csindex = DEFAULT_CS_INDEX; + kay->macsec_csindex = DEFAULT_CS_INDEX; + cs = &cipher_suite_tbl[kay->macsec_csindex]; } else { cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak); if (!cs) { @@ -1608,7 +1581,7 @@ ieee802_1x_mka_decode_dist_sak_body( } sak_len = cs->sak_len; wrap_sak = body->sak + CS_ID_LEN; - participant->kay->macsec_csindex = cs->index; + kay->macsec_csindex = cs->index; } unwrap_sak = os_zalloc(sak_len); @@ -1624,62 +1597,36 @@ ieee802_1x_mka_decode_dist_sak_body( } wpa_hexdump(MSG_DEBUG, "\tAES Key Unwrap of SAK:", unwrap_sak, sak_len); - conf = os_zalloc(sizeof(*conf)); - if (!conf) { - wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); - os_free(unwrap_sak); - return -1; - } - conf->key_len = sak_len; - - conf->key = os_zalloc(conf->key_len); - if (!conf->key) { - wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); + sa_key = os_zalloc(sizeof(*sa_key)); + if (!sa_key) { os_free(unwrap_sak); - os_free(conf); return -1; } - os_memcpy(conf->key, unwrap_sak, conf->key_len); + os_memcpy(&sa_key->key_identifier.mi, &participant->current_peer_id.mi, + MI_LEN); + sa_key->key_identifier.kn = be_to_host32(body->kn); - os_memcpy(&sak_ki.mi, &participant->current_peer_id.mi, - sizeof(sak_ki.mi)); - sak_ki.kn = be_to_host32(body->kn); + sa_key->key = unwrap_sak; + sa_key->key_len = sak_len; - os_memcpy(conf->ki.mi, sak_ki.mi, MI_LEN); - conf->ki.kn = sak_ki.kn; - conf->an = body->dan; - conf->offset = body->confid_offset; - conf->rx = TRUE; - conf->tx = TRUE; - - sa_key = ieee802_1x_kay_init_data_key(conf); - if (!sa_key) { - os_free(unwrap_sak); - os_free(conf->key); - os_free(conf); - return -1; - } + sa_key->confidentiality_offset = body->confid_offset; + sa_key->an = body->dan; + ieee802_1x_kay_init_data_key(sa_key); dl_list_add(&participant->sak_list, &sa_key->list); - ieee802_1x_cp_set_ciphersuite( - participant->kay->cp, - cipher_suite_tbl[participant->kay->macsec_csindex].id); - ieee802_1x_cp_sm_step(participant->kay->cp); - ieee802_1x_cp_set_offset(participant->kay->cp, body->confid_offset); - ieee802_1x_cp_sm_step(participant->kay->cp); - ieee802_1x_cp_set_distributedki(participant->kay->cp, &sak_ki); - ieee802_1x_cp_set_distributedan(participant->kay->cp, body->dan); - ieee802_1x_cp_signal_newsak(participant->kay->cp); - ieee802_1x_cp_sm_step(participant->kay->cp); + ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id); + ieee802_1x_cp_sm_step(kay->cp); + ieee802_1x_cp_set_offset(kay->cp, body->confid_offset); + ieee802_1x_cp_sm_step(kay->cp); + ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier); + ieee802_1x_cp_set_distributedan(kay->cp, body->dan); + ieee802_1x_cp_signal_newsak(kay->cp); + ieee802_1x_cp_sm_step(kay->cp); participant->to_use_sak = TRUE; - os_free(unwrap_sak); - os_free(conf->key); - os_free(conf); - return 0; } @@ -1705,7 +1652,7 @@ ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant) length = sizeof(struct ieee802_1x_mka_icv_body); length += mka_alg_tbl[participant->kay->mka_algindex].icv_len; - return (length + 0x3) & ~0x3; + return MKA_ALIGN_LENGTH(length); } @@ -1733,12 +1680,9 @@ ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant, return -1; } - if (length != DEFAULT_ICV_LEN) { - os_memcpy(wpabuf_put(buf, length - MKA_HDR_LEN), cmac, - length - MKA_HDR_LEN); - } else { - os_memcpy(wpabuf_put(buf, length), cmac, length); - } + if (length != DEFAULT_ICV_LEN) + length -= MKA_HDR_LEN; + os_memcpy(wpabuf_put(buf, length), cmac, length); return 0; } @@ -1754,7 +1698,7 @@ ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant, struct ieee802_1x_mka_icv_body *body; size_t body_len; size_t left_len; - int body_type; + u8 body_type; const u8 *pos; pos = mka_msg; @@ -1801,8 +1745,8 @@ 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 (%d bytes) should be 28 or more octets", - (int) body_len); + "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 28 or more octets", + body_len); return -1; } @@ -1825,8 +1769,8 @@ 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 (%d bytes) should be 5 or more octets", - (int) body_len); + "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 5 or more octets", + body_len); return -1; } @@ -1845,77 +1789,87 @@ static int ieee802_1x_mka_decode_announce_body( } -static struct mka_param_body_handler mak_body_handler[] = { +struct mka_param_body_handler { + int (*body_tx)(struct ieee802_1x_mka_participant *participant, + struct wpabuf *buf); + int (*body_rx)(struct ieee802_1x_mka_participant *participant, + const u8 *mka_msg, size_t msg_len); + int (*body_length)(struct ieee802_1x_mka_participant *participant); + Boolean (*body_present)(struct ieee802_1x_mka_participant *participant); +}; + + +static struct mka_param_body_handler mka_body_handler[] = { /* basic parameter set */ { - ieee802_1x_mka_encode_basic_body, - NULL, - ieee802_1x_mka_basic_body_length, - ieee802_1x_mka_basic_body_present + .body_tx = ieee802_1x_mka_encode_basic_body, + .body_rx = NULL, + .body_length = ieee802_1x_mka_basic_body_length, + .body_present = ieee802_1x_mka_basic_body_present }, /* live peer list parameter set */ { - ieee802_1x_mka_encode_live_peer_body, - ieee802_1x_mka_decode_live_peer_body, - ieee802_1x_mka_get_live_peer_length, - ieee802_1x_mka_live_peer_body_present + .body_tx = ieee802_1x_mka_encode_live_peer_body, + .body_rx = ieee802_1x_mka_decode_live_peer_body, + .body_length = ieee802_1x_mka_get_live_peer_length, + .body_present = ieee802_1x_mka_live_peer_body_present }, /* potential peer list parameter set */ { - ieee802_1x_mka_encode_potential_peer_body, - ieee802_1x_mka_decode_potential_peer_body, - ieee802_1x_mka_get_potential_peer_length, - ieee802_1x_mka_potential_peer_body_present + .body_tx = ieee802_1x_mka_encode_potential_peer_body, + .body_rx = ieee802_1x_mka_decode_potential_peer_body, + .body_length = ieee802_1x_mka_get_potential_peer_length, + .body_present = ieee802_1x_mka_potential_peer_body_present }, /* sak use parameter set */ { - ieee802_1x_mka_encode_sak_use_body, - ieee802_1x_mka_decode_sak_use_body, - ieee802_1x_mka_get_sak_use_length, - ieee802_1x_mka_sak_use_body_present + .body_tx = ieee802_1x_mka_encode_sak_use_body, + .body_rx = ieee802_1x_mka_decode_sak_use_body, + .body_length = ieee802_1x_mka_get_sak_use_length, + .body_present = ieee802_1x_mka_sak_use_body_present }, /* distribute sak parameter set */ { - ieee802_1x_mka_encode_dist_sak_body, - ieee802_1x_mka_decode_dist_sak_body, - ieee802_1x_mka_get_dist_sak_length, - ieee802_1x_mka_dist_sak_body_present + .body_tx = ieee802_1x_mka_encode_dist_sak_body, + .body_rx = ieee802_1x_mka_decode_dist_sak_body, + .body_length = ieee802_1x_mka_get_dist_sak_length, + .body_present = ieee802_1x_mka_dist_sak_body_present }, /* distribute cak parameter set */ { - NULL, - ieee802_1x_mka_decode_dist_cak_body, - NULL, - NULL + .body_tx = NULL, + .body_rx = ieee802_1x_mka_decode_dist_cak_body, + .body_length = NULL, + .body_present = NULL }, /* kmd parameter set */ { - NULL, - ieee802_1x_mka_decode_kmd_body, - NULL, - NULL + .body_tx = NULL, + .body_rx = ieee802_1x_mka_decode_kmd_body, + .body_length = NULL, + .body_present = NULL }, /* announce parameter set */ { - NULL, - ieee802_1x_mka_decode_announce_body, - NULL, - NULL + .body_tx = NULL, + .body_rx = ieee802_1x_mka_decode_announce_body, + .body_length = NULL, + .body_present = NULL }, /* icv parameter set */ { - ieee802_1x_mka_encode_icv_body, - NULL, - ieee802_1x_mka_get_icv_length, - ieee802_1x_mka_icv_body_present + .body_tx = ieee802_1x_mka_encode_icv_body, + .body_rx = NULL, + .body_length = ieee802_1x_mka_get_icv_length, + .body_present = ieee802_1x_mka_icv_body_present }, }; @@ -1923,7 +1877,7 @@ static struct mka_param_body_handler mak_body_handler[] = { /** * ieee802_1x_kay_deinit_data_key - */ -void ieee802_1x_kay_deinit_data_key(struct data_key *pkey) +static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey) { if (!pkey) return; @@ -1945,11 +1899,13 @@ static int ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) { struct data_key *sa_key = NULL; - struct key_conf *conf; struct ieee802_1x_kay_peer *peer; struct ieee802_1x_kay *kay = participant->kay; int ctx_len, ctx_offset; u8 *context; + unsigned int key_len; + u8 *key; + struct macsec_ciphersuite *cs; /* check condition for generating a fresh SAK: * must have one live peer @@ -1976,40 +1932,29 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) return -1; } - conf = os_zalloc(sizeof(*conf)); - if (!conf) { + cs = &cipher_suite_tbl[kay->macsec_csindex]; + key_len = cs->sak_len; + key = os_zalloc(key_len); + if (!key) { wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); return -1; } - conf->key_len = cipher_suite_tbl[kay->macsec_csindex].sak_len; - conf->key = os_zalloc(conf->key_len); - if (!conf->key) { - os_free(conf); - wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); - return -1; - } - - ctx_len = conf->key_len + sizeof(kay->dist_kn); + ctx_len = key_len + sizeof(kay->dist_kn); dl_list_for_each(peer, &participant->live_peers, struct ieee802_1x_kay_peer, list) ctx_len += sizeof(peer->mi); ctx_len += sizeof(participant->mi); context = os_zalloc(ctx_len); - if (!context) { - os_free(conf->key); - os_free(conf); - return -1; - } + if (!context) + goto fail; + ctx_offset = 0; - if (os_get_random(context + ctx_offset, conf->key_len) < 0) { - os_free(context); - os_free(conf->key); - os_free(conf); - return -1; - } - ctx_offset += conf->key_len; + if (os_get_random(context + ctx_offset, key_len) < 0) + goto fail; + + ctx_offset += key_len; dl_list_for_each(peer, &participant->live_peers, struct ieee802_1x_kay_peer, list) { os_memcpy(context + ctx_offset, peer->mi, sizeof(peer->mi)); @@ -2020,46 +1965,44 @@ 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 (conf->key_len == 16) { + if (key_len == 16) { ieee802_1x_sak_128bits_aes_cmac(participant->cak.key, - context, ctx_len, conf->key); - } else if (conf->key_len == 32) { + context, ctx_len, key); + } else if (key_len == 32) { ieee802_1x_sak_128bits_aes_cmac(participant->cak.key, - context, ctx_len, conf->key); + context, ctx_len, key); } else { wpa_printf(MSG_ERROR, "KaY: SAK Length not support"); - os_free(conf->key); - os_free(conf); - os_free(context); - return -1; + goto fail; } - wpa_hexdump(MSG_DEBUG, "KaY: generated new SAK", - conf->key, conf->key_len); - - os_memcpy(conf->ki.mi, participant->mi, MI_LEN); - conf->ki.kn = participant->kay->dist_kn; - conf->an = participant->kay->dist_an; - conf->offset = kay->macsec_confidentiality; - conf->rx = TRUE; - conf->tx = TRUE; + wpa_hexdump(MSG_DEBUG, "KaY: generated new SAK", key, key_len); + os_free(context); + context = NULL; - sa_key = ieee802_1x_kay_init_data_key(conf); + sa_key = os_zalloc(sizeof(*sa_key)); if (!sa_key) { - os_free(conf->key); - os_free(conf); - os_free(context); - return -1; + wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__); + goto fail; } + + sa_key->key = key; + sa_key->key_len = key_len; + os_memcpy(sa_key->key_identifier.mi, participant->mi, MI_LEN); + sa_key->key_identifier.kn = kay->dist_kn; + + sa_key->confidentiality_offset = kay->macsec_confidentiality; + sa_key->an = kay->dist_an; + ieee802_1x_kay_init_data_key(sa_key); + participant->new_key = sa_key; dl_list_add(&participant->sak_list, &sa_key->list); - ieee802_1x_cp_set_ciphersuite(participant->kay->cp, - cipher_suite_tbl[kay->macsec_csindex].id); + ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id); ieee802_1x_cp_sm_step(kay->cp); - ieee802_1x_cp_set_offset(kay->cp, conf->offset); + ieee802_1x_cp_set_offset(kay->cp, kay->macsec_confidentiality); ieee802_1x_cp_sm_step(kay->cp); - ieee802_1x_cp_set_distributedki(kay->cp, &conf->ki); - ieee802_1x_cp_set_distributedan(kay->cp, conf->an); + ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier); + ieee802_1x_cp_set_distributedan(kay->cp, sa_key->an); ieee802_1x_cp_signal_newsak(kay->cp); ieee802_1x_cp_sm_step(kay->cp); @@ -2067,17 +2010,31 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) struct ieee802_1x_kay_peer, list) peer->sak_used = FALSE; - participant->kay->dist_kn++; - participant->kay->dist_an++; - if (participant->kay->dist_an > 3) - participant->kay->dist_an = 0; + kay->dist_kn++; + kay->dist_an++; + if (kay->dist_an > 3) + kay->dist_an = 0; - participant->kay->dist_time = time(NULL); + kay->dist_time = time(NULL); - os_free(conf->key); - os_free(conf); - os_free(context); return 0; + +fail: + os_free(key); + os_free(context); + return -1; +} + + +static int compare_priorities(const struct ieee802_1x_kay_peer *peer, + const struct ieee802_1x_kay_peer *other) +{ + if (peer->key_server_priority < other->key_server_priority) + return -1; + if (other->key_server_priority < peer->key_server_priority) + return 1; + + return os_memcmp(peer->sci.addr, other->sci.addr, ETH_ALEN); } @@ -2092,7 +2049,6 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) struct ieee802_1x_kay_peer *key_server = NULL; struct ieee802_1x_kay *kay = participant->kay; Boolean i_is_key_server; - int i; if (participant->is_obliged_key_server) { participant->new_sak = TRUE; @@ -2112,47 +2068,26 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) continue; } - if (peer->key_server_priority < - key_server->key_server_priority) { + if (compare_priorities(peer, key_server) < 0) key_server = peer; - } else if (peer->key_server_priority == - key_server->key_server_priority) { - for (i = 0; i < 6; i++) { - if (peer->sci.addr[i] < - key_server->sci.addr[i]) - key_server = peer; - } - } } /* elect the key server between me and the above elected peer */ i_is_key_server = FALSE; if (key_server && participant->can_be_key_server) { - if (kay->actor_priority - < key_server->key_server_priority) { - i_is_key_server = TRUE; - } else if (kay->actor_priority - == key_server->key_server_priority) { - for (i = 0; i < 6; i++) { - if (kay->actor_sci.addr[i] - < key_server->sci.addr[i]) { - i_is_key_server = TRUE; - } - } - } - } + struct ieee802_1x_kay_peer tmp; - if (!key_server && !i_is_key_server) { - participant->principal = FALSE; - participant->is_key_server = FALSE; - participant->is_elected = FALSE; - return 0; + tmp.key_server_priority = kay->actor_priority; + os_memcpy(&tmp.sci, &kay->actor_sci, sizeof(tmp.sci)); + if (compare_priorities(&tmp, key_server) < 0) + i_is_key_server = TRUE; + } else if (participant->can_be_key_server) { + i_is_key_server = TRUE; } if (i_is_key_server) { ieee802_1x_cp_set_electedself(kay->cp, TRUE); - if (os_memcmp(&kay->key_server_sci, &kay->actor_sci, - sizeof(kay->key_server_sci))) { + if (!sci_equal(&kay->key_server_sci, &kay->actor_sci)) { ieee802_1x_cp_signal_chgdserver(kay->cp); ieee802_1x_cp_sm_step(kay->cp); } @@ -2167,12 +2102,9 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) os_memcpy(&kay->key_server_sci, &kay->actor_sci, sizeof(kay->key_server_sci)); kay->key_server_priority = kay->actor_priority; - } - - if (key_server) { + } else if (key_server) { ieee802_1x_cp_set_electedself(kay->cp, FALSE); - if (os_memcmp(&kay->key_server_sci, &key_server->sci, - sizeof(kay->key_server_sci))) { + if (!sci_equal(&kay->key_server_sci, &key_server->sci)) { ieee802_1x_cp_signal_chgdserver(kay->cp); ieee802_1x_cp_sm_step(kay->cp); } @@ -2184,6 +2116,10 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) os_memcpy(&kay->key_server_sci, &key_server->sci, sizeof(kay->key_server_sci)); kay->key_server_priority = key_server->key_server_priority; + } else { + participant->principal = FALSE; + participant->is_key_server = FALSE; + participant->is_elected = FALSE; } return 0; @@ -2226,11 +2162,11 @@ ieee802_1x_kay_decide_macsec_use( if (!peer->macsec_desired) continue; - if (peer->macsec_capbility == MACSEC_CAP_NOT_IMPLEMENTED) + if (peer->macsec_capability == MACSEC_CAP_NOT_IMPLEMENTED) continue; - less_capability = (less_capability < peer->macsec_capbility) ? - less_capability : peer->macsec_capbility; + less_capability = (less_capability < peer->macsec_capability) ? + less_capability : peer->macsec_capability; has_peer = TRUE; } @@ -2291,10 +2227,10 @@ ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant, eapol_hdr->type = IEEE802_1X_TYPE_EAPOL_MKA; eapol_hdr->length = host_to_be16(pbuf->size - pbuf->used); - for (i = 0; i < ARRAY_SIZE(mak_body_handler); i++) { - if (mak_body_handler[i].body_present && - mak_body_handler[i].body_present(participant)) { - if (mak_body_handler[i].body_tx(participant, pbuf)) + for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) { + if (mka_body_handler[i].body_present && + mka_body_handler[i].body_present(participant)) { + if (mka_body_handler[i].body_tx(participant, pbuf)) return -1; } } @@ -2316,10 +2252,10 @@ ieee802_1x_participant_send_mkpdu( wpa_printf(MSG_DEBUG, "KaY: to enpacket and send the MKPDU"); length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr); - for (i = 0; i < ARRAY_SIZE(mak_body_handler); i++) { - if (mak_body_handler[i].body_present && - mak_body_handler[i].body_present(participant)) - length += mak_body_handler[i].body_length(participant); + for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) { + if (mka_body_handler[i].body_present && + mka_body_handler[i].body_present(participant)) + length += mka_body_handler[i].body_length(participant); } buf = wpabuf_alloc(length); @@ -2360,27 +2296,16 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) participant = (struct ieee802_1x_mka_participant *)eloop_ctx; kay = participant->kay; if (participant->cak_life) { - if (now > participant->cak_life) { - kay->authenticated = FALSE; - kay->secured = FALSE; - kay->failed = TRUE; - ieee802_1x_kay_delete_mka(kay, &participant->ckn); - return; - } + if (now > participant->cak_life) + goto delete_mka; } /* should delete MKA instance if there are not live peers * when the MKA life elapsed since its creating */ if (participant->mka_life) { if (dl_list_empty(&participant->live_peers)) { - if (now > participant->mka_life) { - kay->authenticated = FALSE; - kay->secured = FALSE; - kay->failed = TRUE; - ieee802_1x_kay_delete_mka(kay, - &participant->ckn); - return; - } + if (now > participant->mka_life) + goto delete_mka; } else { participant->mka_life = 0; } @@ -2397,8 +2322,7 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) dl_list_for_each_safe(rxsc, pre_rxsc, &participant->rxsc_list, struct receive_sc, list) { - if (os_memcmp(&rxsc->sci, &peer->sci, - sizeof(rxsc->sci)) == 0) { + if (sci_equal(&rxsc->sci, &peer->sci)) { secy_delete_receive_sc(kay, rxsc); ieee802_1x_kay_deinit_receive_sc( participant, rxsc); @@ -2469,6 +2393,14 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) eloop_register_timeout(MKA_HELLO_TIME / 1000, 0, ieee802_1x_participant_timer, participant, NULL); + + return; + +delete_mka: + kay->authenticated = FALSE; + kay->secured = FALSE; + kay->failed = TRUE; + ieee802_1x_kay_delete_mka(kay, &participant->ckn); } @@ -2506,8 +2438,8 @@ ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN, dl_list_add(&psc->sa_list, &psa->list); wpa_printf(MSG_DEBUG, - "KaY: Create transmit SA(an: %d, next_PN: %u) of SC(channel: %d)", - (int) an, next_PN, psc->channel); + "KaY: Create transmit SA(an: %hhu, next_PN: %u) of SC(channel: %d)", + an, next_PN, psc->channel); return psa; } @@ -2520,8 +2452,8 @@ static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa) { psa->pkey = NULL; wpa_printf(MSG_DEBUG, - "KaY: Delete transmit SA(an: %d) of SC(channel: %d)", - psa->an, psa->sc->channel); + "KaY: Delete transmit SA(an: %hhu) of SC", + psa->an); dl_list_del(&psa->list); os_free(psa); } @@ -2837,38 +2769,6 @@ int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay) /** - * ieee802_1x_kay_cp_conf - - */ -int ieee802_1x_kay_cp_conf(struct ieee802_1x_kay *kay, - struct ieee802_1x_cp_conf *pconf) -{ - pconf->protect = kay->macsec_protect; - pconf->replay_protect = kay->macsec_replay_protect; - pconf->validate = kay->macsec_validate; - - return 0; -} - - -/** - * ieee802_1x_kay_alloc_cp_sm - - */ -static struct ieee802_1x_cp_sm * -ieee802_1x_kay_alloc_cp_sm(struct ieee802_1x_kay *kay) -{ - struct ieee802_1x_cp_conf conf; - - os_memset(&conf, 0, sizeof(conf)); - conf.protect = kay->macsec_protect; - conf.replay_protect = kay->macsec_replay_protect; - conf.validate = kay->macsec_validate; - conf.replay_window = kay->macsec_replay_window; - - return ieee802_1x_cp_sm_init(kay, &conf); -} - - -/** * ieee802_1x_kay_mkpdu_sanity_check - * sanity check specified in clause 11.11.2 of IEEE802.1X-2010 */ @@ -2896,13 +2796,13 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, return -1; } - /* MKPDU should not less than 32 octets */ + /* MKPDU should 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"); return -1; } - /* MKPDU should multiple 4 octets */ + /* MKPDU should be a multiple of 4 octets */ if ((mka_msg_len % 4) != 0) { wpa_printf(MSG_MSGDUMP, "KaY: MKPDU is not multiple of 4 octets"); @@ -2915,9 +2815,9 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, /* EAPOL-MKA body should comprise basic parameter set and ICV */ if (mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN) { wpa_printf(MSG_ERROR, - "KaY: Received EAPOL-MKA Packet Body Length (%d bytes) is less than the Basic Parameter Set Header Length (%d bytes) + the Basic Parameter Set Body Length (%d bytes) + %d bytes of ICV", - (int) mka_msg_len, (int) MKA_HDR_LEN, - (int) body_len, DEFAULT_ICV_LEN); + "KaY: Received EAPOL-MKA Packet Body Length (%zu bytes) is less than the Basic Parameter Set Header Length (%zu bytes) + the Basic Parameter Set Body Length (%zu bytes) + %d bytes of ICV", + mka_msg_len, MKA_HDR_LEN, + body_len, DEFAULT_ICV_LEN); return -1; } @@ -2948,21 +2848,19 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, wpa_printf(MSG_ERROR, "KaY: omac1_aes_128 failed"); return -1; } + msg_icv = ieee802_1x_mka_decode_icv_body(participant, (u8 *) mka_hdr, mka_msg_len); - - if (msg_icv) { - if (os_memcmp_const(msg_icv, icv, - mka_alg_tbl[kay->mka_algindex].icv_len) != - 0) { - wpa_printf(MSG_ERROR, - "KaY: Computed ICV is not equal to Received ICV"); - return -1; - } - } else { + if (!msg_icv) { wpa_printf(MSG_ERROR, "KaY: No ICV"); return -1; } + if (os_memcmp_const(msg_icv, icv, + mka_alg_tbl[kay->mka_algindex].icv_len) != 0) { + wpa_printf(MSG_ERROR, + "KaY: Computed ICV is not equal to Received ICV"); + return -1; + } return 0; } @@ -2978,10 +2876,9 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, struct ieee802_1x_mka_hdr *hdr; size_t body_len; size_t left_len; - int body_type; + u8 body_type; int i; const u8 *pos; - Boolean my_included; Boolean handled[256]; if (ieee802_1x_kay_mkpdu_sanity_check(kay, buf, len)) @@ -3002,28 +2899,27 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, left_len -= body_len + MKA_HDR_LEN; /* check i am in the peer's peer list */ - my_included = ieee802_1x_mka_i_in_peerlist(participant, pos, left_len); - if (my_included) { + if (ieee802_1x_mka_i_in_peerlist(participant, pos, left_len) && + !ieee802_1x_kay_is_in_live_peer(participant, + participant->current_peer_id.mi)) { /* accept the peer as live peer */ - if (!ieee802_1x_kay_is_in_peer( - participant, - participant->current_peer_id.mi)) { - if (!ieee802_1x_kay_create_live_peer( + if (ieee802_1x_kay_is_in_potential_peer( + participant, participant->current_peer_id.mi)) { + if (!ieee802_1x_kay_move_live_peer( participant, participant->current_peer_id.mi, - participant->current_peer_id.mn)) + be_to_host32(participant-> + current_peer_id.mn))) + return -1; + } else if (!ieee802_1x_kay_create_live_peer( + participant, participant->current_peer_id.mi, + be_to_host32(participant-> + current_peer_id.mn))) { return -1; - ieee802_1x_kay_elect_key_server(participant); - ieee802_1x_kay_decide_macsec_use(participant); - } - if (ieee802_1x_kay_is_in_potential_peer( - participant, participant->current_peer_id.mi)) { - ieee802_1x_kay_move_live_peer( - participant, participant->current_peer_id.mi, - participant->current_peer_id.mn); - ieee802_1x_kay_elect_key_server(participant); - ieee802_1x_kay_decide_macsec_use(participant); } + + ieee802_1x_kay_elect_key_server(participant); + ieee802_1x_kay_decide_macsec_use(participant); } /* @@ -3034,7 +2930,9 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, handled[i] = FALSE; handled[0] = TRUE; - while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) { + for (; left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN; + 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_type = get_mka_param_body_type(hdr); @@ -3044,28 +2942,25 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) { wpa_printf(MSG_ERROR, - "KaY: MKA Peer Packet Body Length (%d bytes) is less than the Parameter Set Header Length (%d bytes) + the Parameter Set Body Length (%d bytes) + %d bytes of ICV", - (int) left_len, (int) MKA_HDR_LEN, - (int) body_len, DEFAULT_ICV_LEN); - goto next_para_set; + "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV", + left_len, MKA_HDR_LEN, + body_len, DEFAULT_ICV_LEN); + continue; } if (handled[body_type]) - goto next_para_set; + continue; handled[body_type] = TRUE; - if (mak_body_handler[body_type].body_rx) { - mak_body_handler[body_type].body_rx + 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); } else { wpa_printf(MSG_ERROR, - "The type %d not supported in this MKA version %d", + "The type %d is not supported in this MKA version %d", body_type, MKA_VERSION_ID); } - -next_para_set: - pos += body_len + MKA_HDR_LEN; - left_len -= body_len + MKA_HDR_LEN; } kay->active = TRUE; @@ -3094,10 +2989,10 @@ 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) + - ntohs(eapol_hdr->length)) { + be_to_host16(eapol_hdr->length)) { wpa_printf(MSG_MSGDUMP, "KAY: EAPOL MPDU is invalid: (%lu-%lu)", (unsigned long) len, - (unsigned long) ntohs(eapol_hdr->length)); + (unsigned long) be_to_host16(eapol_hdr->length)); return; } @@ -3106,7 +3001,7 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, eapol_hdr->version); return; } - if (ntohs(eth_hdr->ethertype) != ETH_P_PAE || + if (be_to_host16(eth_hdr->ethertype) != ETH_P_PAE || eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA) return; @@ -3147,7 +3042,7 @@ 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 = 0x0001; + kay->actor_sci.port = host_to_be16(0x0001); kay->actor_priority = DEFAULT_PRIO_NOT_KEY_SERVER; /* While actor acts as a key server, shall distribute sakey */ @@ -3192,7 +3087,7 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, wpa_printf(MSG_DEBUG, "KaY: secy init macsec done"); /* init CP */ - kay->cp = ieee802_1x_kay_alloc_cp_sm(kay); + kay->cp = ieee802_1x_cp_sm_init(kay); if (kay->cp == NULL) { ieee802_1x_kay_deinit(kay); return NULL; @@ -3314,7 +3209,7 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn, default: participant->is_obliged_key_server = FALSE; participant->can_be_key_server = TRUE; - participant->is_key_server = FALSE; + participant->is_key_server = TRUE; participant->is_elected = FALSE; break; } @@ -3335,9 +3230,8 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn, participant->retry_count = 0; participant->kay = kay; - if (os_get_random(participant->mi, sizeof(participant->mi)) < 0) + if (!reset_participant_mi(participant)) goto fail; - participant->mn = 0; participant->lrx = FALSE; participant->ltx = FALSE; @@ -3422,6 +3316,7 @@ ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn) return; } + eloop_cancel_timeout(ieee802_1x_participant_timer, participant, NULL); dl_list_del(&participant->list); /* remove live peer */ @@ -3510,14 +3405,15 @@ ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay) * ieee802_1x_kay_change_cipher_suite - */ int -ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, int cs_index) +ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, + unsigned int cs_index) { struct ieee802_1x_mka_participant *participant; if (!kay) return -1; - if ((unsigned int) cs_index >= CS_TABLE_SIZE) { + if (cs_index >= CS_TABLE_SIZE) { wpa_printf(MSG_ERROR, "KaY: Configured cipher suite index is out of range"); return -1; diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h index 064417ea51ad0..afbaa336cbda6 100644 --- a/src/pae/ieee802_1x_kay.h +++ b/src/pae/ieee802_1x_kay.h @@ -14,7 +14,6 @@ #include "common/ieee802_1x_defs.h" struct macsec_init_params; -struct ieee802_1x_cp_conf; #define MI_LEN 12 #define MAX_KEY_LEN 32 /* 32 bytes, 256 bits */ @@ -32,7 +31,7 @@ struct ieee802_1x_mka_ki { struct ieee802_1x_mka_sci { u8 addr[ETH_ALEN]; - u16 port; + be16 port; }; struct mka_key { @@ -48,8 +47,6 @@ struct mka_key_name { enum mka_created_mode { PSK, EAP_EXCHANGE, - DISTRIBUTED, - CACHED, }; struct ieee802_1x_kay_ctx { @@ -61,7 +58,7 @@ struct ieee802_1x_kay_ctx { int (*macsec_deinit)(void *ctx); int (*enable_protect_frames)(void *ctx, Boolean enabled); int (*set_replay_protect)(void *ctx, Boolean enabled, u32 window); - int (*set_current_cipher_suite)(void *ctx, const u8 *cs, size_t cs_len); + int (*set_current_cipher_suite)(void *ctx, u64 cs); int (*enable_controlled_port)(void *ctx, Boolean enabled); int (*get_receive_lowest_pn)(void *ctx, u32 channel, u8 an, u32 *lowest_pn); @@ -126,7 +123,7 @@ struct ieee802_1x_kay { Boolean is_obliged_key_server; char if_name[IFNAMSIZ]; - int macsec_csindex; /* MACsec cipher suite table index */ + unsigned int macsec_csindex; /* MACsec cipher suite table index */ int mka_algindex; /* MKA alg table index */ u32 dist_kn; @@ -171,7 +168,7 @@ void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay, Boolean status); int ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay); int ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, - int cs_index); + unsigned int cs_index); int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay, struct ieee802_1x_mka_ki *lki, u8 lan, @@ -188,7 +185,5 @@ int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay, int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay, struct ieee802_1x_mka_ki *lki); int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay); -int ieee802_1x_kay_cp_conf(struct ieee802_1x_kay *kay, - struct ieee802_1x_cp_conf *pconf); #endif /* IEEE802_1X_KAY_H */ diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h index bdad3a5beb137..622282e97c513 100644 --- a/src/pae/ieee802_1x_kay_i.h +++ b/src/pae/ieee802_1x_kay_i.h @@ -38,7 +38,7 @@ struct ieee802_1x_kay; struct ieee802_1x_mka_peer_id { u8 mi[MI_LEN]; - u32 mn; + be32 mn; }; struct ieee802_1x_kay_peer { @@ -49,21 +49,11 @@ struct ieee802_1x_kay_peer { Boolean is_key_server; u8 key_server_priority; Boolean macsec_desired; - enum macsec_cap macsec_capbility; + enum macsec_cap macsec_capability; Boolean sak_used; struct dl_list list; }; -struct key_conf { - u8 *key; - struct ieee802_1x_mka_ki ki; - enum confidentiality_offset offset; - u8 an; - Boolean tx; - Boolean rx; - int key_len; /* unit: byte */ -}; - struct data_key { u8 *key; int key_len; @@ -147,7 +137,7 @@ struct receive_sa { }; struct macsec_ciphersuite { - u8 id[CS_ID_LEN]; + u64 id; char name[32]; enum macsec_cap capable; int sak_len; /* unit: byte */ @@ -241,48 +231,48 @@ struct ieee802_1x_mka_participant { struct ieee802_1x_mka_hdr { /* octet 1 */ - u32 type:8; + u8 type; /* octet 2 */ - u32 reserve:8; + u8 reserve; /* octet 3 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 length:4; - u32 reserve1:4; + u8 length:4; + u8 reserve1:4; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 reserve1:4; - u32 length:4; + u8 reserve1:4; + u8 length:4; #else #error "Please fix <bits/endian.h>" #endif /* octet 4 */ - u32 length1:8; + u8 length1; }; #define MKA_HDR_LEN sizeof(struct ieee802_1x_mka_hdr) struct ieee802_1x_mka_basic_body { /* octet 1 */ - u32 version:8; + u8 version; /* octet 2 */ - u32 priority:8; + u8 priority; /* octet 3 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 length:4; - u32 macsec_capbility:2; - u32 macsec_desired:1; - u32 key_server:1; + u8 length:4; + u8 macsec_capability:2; + u8 macsec_desired:1; + u8 key_server:1; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 key_server:1; - u32 macsec_desired:1; - u32 macsec_capbility:2; - u32 length:4; + u8 key_server:1; + u8 macsec_desired:1; + u8 macsec_capability:2; + u8 length:4; #endif /* octet 4 */ - u32 length1:8; + u8 length1; struct ieee802_1x_mka_sci actor_sci; u8 actor_mi[MI_LEN]; - u32 actor_mn; + be32 actor_mn; u8 algo_agility[4]; /* followed by CAK Name*/ @@ -291,19 +281,19 @@ struct ieee802_1x_mka_basic_body { struct ieee802_1x_mka_peer_body { /* octet 1 */ - u32 type:8; + u8 type; /* octet 2 */ - u32 reserve:8; + u8 reserve; /* octet 3 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 length:4; - u32 reserve1:4; + u8 length:4; + u8 reserve1:4; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 reserve1:4; - u32 length:4; + u8 reserve1:4; + u8 length:4; #endif /* octet 4 */ - u32 length1:8; + u8 length1; u8 peer[0]; /* followed by Peers */ @@ -311,83 +301,83 @@ struct ieee802_1x_mka_peer_body { struct ieee802_1x_mka_sak_use_body { /* octet 1 */ - u32 type:8; + u8 type; /* octet 2 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 orx:1; - u32 otx:1; - u32 oan:2; - u32 lrx:1; - u32 ltx:1; - u32 lan:2; + u8 orx:1; + u8 otx:1; + u8 oan:2; + u8 lrx:1; + u8 ltx:1; + u8 lan:2; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 lan:2; - u32 ltx:1; - u32 lrx:1; - u32 oan:2; - u32 otx:1; - u32 orx:1; + u8 lan:2; + u8 ltx:1; + u8 lrx:1; + u8 oan:2; + u8 otx:1; + u8 orx:1; #endif /* octet 3 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 length:4; - u32 delay_protect:1; - u32 reserve:1; - u32 prx:1; - u32 ptx:1; + u8 length:4; + u8 delay_protect:1; + u8 reserve:1; + u8 prx:1; + u8 ptx:1; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 ptx:1; - u32 prx:1; - u32 reserve:1; - u32 delay_protect:1; - u32 length:4; + u8 ptx:1; + u8 prx:1; + u8 reserve:1; + u8 delay_protect:1; + u8 length:4; #endif /* octet 4 */ - u32 length1:8; + u8 length1; /* octet 5 - 16 */ u8 lsrv_mi[MI_LEN]; /* octet 17 - 20 */ - u32 lkn; + be32 lkn; /* octet 21 - 24 */ - u32 llpn; + be32 llpn; /* octet 25 - 36 */ u8 osrv_mi[MI_LEN]; /* octet 37 - 40 */ - u32 okn; + be32 okn; /* octet 41 - 44 */ - u32 olpn; + be32 olpn; }; struct ieee802_1x_mka_dist_sak_body { /* octet 1 */ - u32 type:8; + u8 type; /* octet 2 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 reserve:4; - u32 confid_offset:2; - u32 dan:2; + u8 reserve:4; + u8 confid_offset:2; + u8 dan:2; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 dan:2; - u32 confid_offset:2; - u32 reserve:4; + u8 dan:2; + u8 confid_offset:2; + u8 reserve:4; #endif /* octet 3 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 length:4; - u32 reserve1:4; + u8 length:4; + u8 reserve1:4; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 reserve1:4; - u32 length:4; + u8 reserve1:4; + u8 length:4; #endif /* octet 4 */ - u32 length1:8; + u8 length1; /* octet 5 - 8 */ - u32 kn; + be32 kn; /* for GCM-AES-128: octet 9-32: SAK * for other cipher suite: octet 9-16: cipher suite id, octet 17-: SAK @@ -398,19 +388,19 @@ struct ieee802_1x_mka_dist_sak_body { struct ieee802_1x_mka_icv_body { /* octet 1 */ - u32 type:8; + u8 type; /* octet 2 */ - u32 reserve:8; + u8 reserve; /* octet 3 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - u32 length:4; - u32 reserve1:4; + u8 length:4; + u8 reserve1:4; #elif __BYTE_ORDER == __BIG_ENDIAN - u32 reserve1:4; - u32 length:4; + u8 reserve1:4; + u8 length:4; #endif /* octet 4 */ - u32 length1:8; + u8 length1; /* octet 5 - */ u8 icv[0]; diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c index fbe05dc35d7cb..2d12911dbfcfd 100644 --- a/src/pae/ieee802_1x_secy_ops.c +++ b/src/pae/ieee802_1x_secy_ops.c @@ -65,8 +65,7 @@ int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean enabled, u32 win) } -int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, - const u8 *cs, size_t cs_len) +int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, u64 cs) { struct ieee802_1x_kay_ctx *ops; @@ -82,7 +81,7 @@ int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, return -1; } - return ops->set_current_cipher_suite(ops->ctx, cs, cs_len); + return ops->set_current_cipher_suite(ops->ctx, cs); } diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h index 295b823a9d7f3..f5057ee119583 100644 --- a/src/pae/ieee802_1x_secy_ops.h +++ b/src/pae/ieee802_1x_secy_ops.h @@ -26,8 +26,7 @@ int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay, enum validate_frames vf); int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean flag); int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean flag, u32 win); -int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, - const u8 *cs, size_t cs_len); +int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, u64 cs); int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay, enum confidentiality_offset co); int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean flag); diff --git a/src/radius/radius.c b/src/radius/radius.c index 1ebfd11f3b9a2..407e4f8b96149 100644 --- a/src/radius/radius.c +++ b/src/radius/radius.c @@ -1,6 +1,6 @@ /* * RADIUS message processing - * Copyright (c) 2002-2009, 2011-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2009, 2011-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -173,6 +173,8 @@ static const struct radius_attr_type radius_attrs[] = { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP }, { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_SERVICE_TYPE, "Service-Type", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_FRAMED_IP_ADDRESS, "Framed-IP-Address", RADIUS_ATTR_IP }, { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST }, @@ -214,6 +216,7 @@ static const struct radius_attr_type radius_attrs[] = RADIUS_ATTR_INT32 }, { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_EGRESS_VLANID, "EGRESS-VLANID", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type", @@ -535,7 +538,8 @@ int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, - size_t secret_len) + size_t secret_len, + int require_message_authenticator) { const u8 *addr[4]; size_t len[4]; @@ -574,7 +578,11 @@ int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, } if (attr == NULL) { - /* Message-Authenticator is MAY; not required */ + if (require_message_authenticator) { + wpa_printf(MSG_WARNING, + "Missing Message-Authenticator attribute in RADIUS message"); + return 1; + } return 0; } @@ -703,7 +711,7 @@ struct radius_msg * radius_msg_parse(const u8 *data, size_t len) attr = (struct radius_attr_hdr *) pos; - if (pos + attr->length > end || attr->length < sizeof(*attr)) + if (attr->length > end - pos || attr->length < sizeof(*attr)) goto fail; /* TODO: check that attr->length is suitable for attr->type */ @@ -815,8 +823,9 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, os_memcpy(msg->hdr->authenticator, req_auth, sizeof(msg->hdr->authenticator)); } - hmac_md5(secret, secret_len, wpabuf_head(msg->buf), - wpabuf_len(msg->buf), auth); + if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf), + wpabuf_len(msg->buf), auth) < 0) + return 1; os_memcpy(attr + 1, orig, MD5_MAC_LEN); if (req_auth) { os_memcpy(msg->hdr->authenticator, orig_authenticator, @@ -859,8 +868,8 @@ int radius_msg_verify(struct radius_msg *msg, const u8 *secret, len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); addr[3] = secret; len[3] = secret_len; - md5_vector(4, addr, len, hash); - if (os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { + if (md5_vector(4, addr, len, hash) < 0 || + os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { wpa_printf(MSG_INFO, "Response Authenticator invalid!"); return 1; } @@ -892,25 +901,11 @@ int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, /* Create Request Authenticator. The value should be unique over the lifetime * of the shared secret between authenticator and authentication server. - * Use one-way MD5 hash calculated from current timestamp and some data given - * by the caller. */ -void radius_msg_make_authenticator(struct radius_msg *msg, - const u8 *data, size_t len) + */ +int radius_msg_make_authenticator(struct radius_msg *msg) { - struct os_time tv; - long int l; - const u8 *addr[3]; - size_t elen[3]; - - os_get_time(&tv); - l = os_random(); - addr[0] = (u8 *) &tv; - elen[0] = sizeof(tv); - addr[1] = data; - elen[1] = len; - addr[2] = (u8 *) &l; - elen[2] = sizeof(l); - md5_vector(3, addr, elen, msg->hdr->authenticator); + return os_get_random((u8 *) &msg->hdr->authenticator, + sizeof(msg->hdr->authenticator)); } @@ -1028,7 +1023,10 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, addr[1] = pos - MD5_MAC_LEN; elen[1] = MD5_MAC_LEN; } - md5_vector(first ? 3 : 2, addr, elen, hash); + if (md5_vector(first ? 3 : 2, addr, elen, hash) < 0) { + os_free(plain); + return NULL; + } first = 0; for (i = 0; i < MD5_MAC_LEN; i++) @@ -1210,7 +1208,11 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, vhdr = (struct radius_attr_vendor *) pos; vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY; pos = (u8 *) (vhdr + 1); - salt = os_random() | 0x8000; + if (os_get_random((u8 *) &salt, sizeof(salt)) < 0) { + os_free(buf); + return 0; + } + salt |= 0x8000; WPA_PUT_BE16(pos, salt); pos += 2; encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret, @@ -1422,12 +1424,30 @@ struct radius_tunnel_attrs { }; +static int cmp_int(const void *a, const void *b) +{ + int x, y; + + x = *((int *) a); + y = *((int *) b); + return (x - y); +} + + /** * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information + * The k tagged vlans found are sorted by vlan_id and stored in the first k + * items of tagged. + * * @msg: RADIUS message - * Returns: VLAN ID for the first tunnel configuration or 0 if none is found + * @untagged: Pointer to store untagged vid + * @numtagged: Size of tagged + * @tagged: Pointer to store tagged list + * + * Returns: 0 if neither tagged nor untagged configuration is found, 1 otherwise */ -int radius_msg_get_vlanid(struct radius_msg *msg) +int radius_msg_get_vlanid(struct radius_msg *msg, int *untagged, int numtagged, + int *tagged) { struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun; size_t i; @@ -1435,8 +1455,12 @@ int radius_msg_get_vlanid(struct radius_msg *msg) const u8 *data; char buf[10]; size_t dlen; + int j, taggedidx = 0, vlan_id; os_memset(&tunnel, 0, sizeof(tunnel)); + for (j = 0; j < numtagged; j++) + tagged[j] = 0; + *untagged = 0; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); @@ -1473,21 +1497,44 @@ int radius_msg_get_vlanid(struct radius_msg *msg) break; os_memcpy(buf, data, dlen); buf[dlen] = '\0'; + vlan_id = atoi(buf); + if (vlan_id <= 0) + break; tun->tag_used++; - tun->vlanid = atoi(buf); + tun->vlanid = vlan_id; + break; + case RADIUS_ATTR_EGRESS_VLANID: /* RFC 4675 */ + if (attr->length != 6) + break; + vlan_id = WPA_GET_BE24(data + 1); + if (vlan_id <= 0) + break; + if (data[0] == 0x32) + *untagged = vlan_id; + else if (data[0] == 0x31 && tagged && + taggedidx < numtagged) + tagged[taggedidx++] = vlan_id; break; } } + /* Use tunnel with the lowest tag for untagged VLAN id */ for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) { tun = &tunnel[i]; if (tun->tag_used && tun->type == RADIUS_TUNNEL_TYPE_VLAN && tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 && - tun->vlanid > 0) - return tun->vlanid; + tun->vlanid > 0) { + *untagged = tun->vlanid; + break; + } } + if (taggedidx) + qsort(tagged, taggedidx, sizeof(int), cmp_int); + + if (*untagged > 0 || taggedidx) + return 1; return 0; } @@ -1669,3 +1716,14 @@ u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs) return 0; } + + +int radius_gen_session_id(u8 *id, size_t len) +{ + /* + * Acct-Session-Id and Acct-Multi-Session-Id should be globally and + * temporarily unique. A high quality random number is required + * therefore. This could be be improved by switching to a GUID. + */ + return os_get_random(id, len); +} diff --git a/src/radius/radius.h b/src/radius/radius.h index 5977339e08d2b..cd510d2c88e22 100644 --- a/src/radius/radius.h +++ b/src/radius/radius.h @@ -1,6 +1,6 @@ /* * RADIUS message processing - * Copyright (c) 2002-2009, 2012, 2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2009, 2012, 2014-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -52,6 +52,8 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_USER_PASSWORD = 2, RADIUS_ATTR_NAS_IP_ADDRESS = 4, RADIUS_ATTR_NAS_PORT = 5, + RADIUS_ATTR_SERVICE_TYPE = 6, + RADIUS_ATTR_FRAMED_IP_ADDRESS = 8, RADIUS_ATTR_FRAMED_MTU = 12, RADIUS_ATTR_REPLY_MESSAGE = 18, RADIUS_ATTR_STATE = 24, @@ -79,6 +81,7 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS = 52, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53, RADIUS_ATTR_EVENT_TIMESTAMP = 55, + RADIUS_ATTR_EGRESS_VLANID = 56, RADIUS_ATTR_NAS_PORT_TYPE = 61, RADIUS_ATTR_TUNNEL_TYPE = 64, RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65, @@ -108,6 +111,9 @@ enum { RADIUS_ATTR_USER_NAME = 1, }; +/* Service-Type values (RFC 2865, 5.6) */ +#define RADIUS_SERVICE_TYPE_FRAMED 2 + /* Termination-Action */ #define RADIUS_TERMINATION_ACTION_DEFAULT 0 #define RADIUS_TERMINATION_ACTION_RADIUS_REQUEST 1 @@ -236,7 +242,8 @@ void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret, int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, size_t secret_len); int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, - size_t secret_len); + size_t secret_len, + int require_message_authenticator); struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type, const u8 *data, size_t data_len); struct radius_msg * radius_msg_parse(const u8 *data, size_t len); @@ -250,8 +257,7 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, size_t secret_len, const u8 *req_auth); int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, u8 type); -void radius_msg_make_authenticator(struct radius_msg *msg, - const u8 *data, size_t len); +int radius_msg_make_authenticator(struct radius_msg *msg); struct radius_ms_mppe_keys * radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, const u8 *secret, size_t secret_len); @@ -274,7 +280,8 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, const u8 *data, size_t data_len, const u8 *secret, size_t secret_len); int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len); -int radius_msg_get_vlanid(struct radius_msg *msg); +int radius_msg_get_vlanid(struct radius_msg *msg, int *untagged, int numtagged, + int *tagged); char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, const u8 *secret, size_t secret_len, struct radius_msg *sent_msg, size_t n); @@ -319,4 +326,6 @@ int radius_copy_class(struct radius_class_data *dst, u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs); +int radius_gen_session_id(u8 *id, size_t len); + #endif /* RADIUS_H */ diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index 693f61ea04552..06c804d132fd5 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -226,6 +226,16 @@ struct radius_client_data { * next_radius_identifier - Next RADIUS message identifier to use */ u8 next_radius_identifier; + + /** + * interim_error_cb - Interim accounting error callback + */ + void (*interim_error_cb)(const u8 *addr, void *ctx); + + /** + * interim_error_cb_ctx - interim_error_cb() context data + */ + void *interim_error_cb_ctx; }; @@ -297,6 +307,25 @@ int radius_client_register(struct radius_client_data *radius, } +/** + * radius_client_set_interim_erro_cb - Register an interim acct error callback + * @radius: RADIUS client context from radius_client_init() + * @addr: Station address from the failed message + * @cb: Handler for interim accounting errors + * @ctx: Context pointer for handler callbacks + * + * This function is used to register a handler for processing failed + * transmission attempts of interim accounting update messages. + */ +void radius_client_set_interim_error_cb(struct radius_client_data *radius, + void (*cb)(const u8 *addr, void *ctx), + void *ctx) +{ + radius->interim_error_cb = cb; + radius->interim_error_cb_ctx = ctx; +} + + /* * Returns >0 if message queue was flushed (i.e., the message that triggered * the error is not available anymore) @@ -308,7 +337,7 @@ static int radius_client_handle_send_error(struct radius_client_data *radius, int _errno = errno; wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno)); if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || - _errno == EBADF || _errno == ENETUNREACH) { + _errno == EBADF || _errno == ENETUNREACH || _errno == EACCES) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "Send failed - maybe interface status changed -" @@ -336,6 +365,8 @@ static int radius_client_retransmit(struct radius_client_data *radius, int s; struct wpabuf *buf; size_t prev_num_msgs; + u8 *acct_delay_time; + size_t acct_delay_time_len; if (entry->msg_type == RADIUS_ACCT || entry->msg_type == RADIUS_ACCT_INTERIM) { @@ -371,12 +402,52 @@ static int radius_client_retransmit(struct radius_client_data *radius, conf->auth_server->retransmissions++; } } + + if (entry->msg_type == RADIUS_ACCT_INTERIM) { + wpa_printf(MSG_DEBUG, + "RADIUS: Failed to transmit interim accounting update to " + MACSTR " - drop message and request a new update", + MAC2STR(entry->addr)); + if (radius->interim_error_cb) + radius->interim_error_cb(entry->addr, + radius->interim_error_cb_ctx); + return 1; + } + if (s < 0) { wpa_printf(MSG_INFO, "RADIUS: No valid socket for retransmission"); return 1; } + if (entry->msg_type == RADIUS_ACCT && + radius_msg_get_attr_ptr(entry->msg, RADIUS_ATTR_ACCT_DELAY_TIME, + &acct_delay_time, &acct_delay_time_len, + NULL) == 0 && + acct_delay_time_len == 4) { + struct radius_hdr *hdr; + u32 delay_time; + + /* + * Need to assign a new identifier since attribute contents + * changes. + */ + hdr = radius_msg_get_hdr(entry->msg); + hdr->identifier = radius_client_get_id(radius); + + /* Update Acct-Delay-Time to show wait time in queue */ + delay_time = now - entry->first_try; + WPA_PUT_BE32(acct_delay_time, delay_time); + + wpa_printf(MSG_DEBUG, + "RADIUS: Updated Acct-Delay-Time to %u for retransmission", + delay_time); + radius_msg_finish_acct(entry->msg, entry->shared_secret, + entry->shared_secret_len); + if (radius->conf->msg_dumps) + radius_msg_dump(entry->msg); + } + /* retransmit; remove entry if too many attempts */ entry->attempts++; hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, @@ -407,7 +478,6 @@ static int radius_client_retransmit(struct radius_client_data *radius, static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) { struct radius_client_data *radius = eloop_ctx; - struct hostapd_radius_servers *conf = radius->conf; struct os_reltime now; os_time_t first; struct radius_msg_list *entry, *prev, *tmp; @@ -476,10 +546,10 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) (long int) (first - now.sec)); } - if (auth_failover && conf->num_auth_servers > 1) + if (auth_failover) radius_client_auth_failover(radius); - if (acct_failover && conf->num_acct_servers > 1) + if (acct_failover) radius_client_acct_failover(radius); } @@ -625,39 +695,6 @@ static void radius_client_list_add(struct radius_client_data *radius, } -static void radius_client_list_del(struct radius_client_data *radius, - RadiusType msg_type, const u8 *addr) -{ - struct radius_msg_list *entry, *prev, *tmp; - - if (addr == NULL) - return; - - entry = radius->msgs; - prev = NULL; - while (entry) { - if (entry->msg_type == msg_type && - os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { - if (prev) - prev->next = entry->next; - else - radius->msgs = entry->next; - tmp = entry; - entry = entry->next; - hostapd_logger(radius->ctx, addr, - HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, - "Removing matching RADIUS message"); - radius_client_msg_free(tmp); - radius->num_msgs--; - continue; - } - prev = entry; - entry = entry->next; - } -} - - /** * radius_client_send - Send a RADIUS request * @radius: RADIUS client context from radius_client_init() @@ -669,16 +706,19 @@ static void radius_client_list_del(struct radius_client_data *radius, * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference * between accounting and interim accounting messages is that the interim - * message will override any pending interim accounting updates while a new - * accounting message does not remove any pending messages. + * message will not be retransmitted. Instead, a callback is used to indicate + * that the transmission failed for the specific station @addr so that a new + * interim accounting update message can be generated with up-to-date session + * data instead of trying to resend old information. * * 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. + * (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. * * The related device MAC address can be used to identify pending messages that - * can be removed with radius_client_flush_auth() or with interim accounting - * updates. + * can be removed with radius_client_flush_auth(). */ int radius_client_send(struct radius_client_data *radius, struct radius_msg *msg, RadiusType msg_type, @@ -691,11 +731,6 @@ int radius_client_send(struct radius_client_data *radius, int s, res; struct wpabuf *buf; - if (msg_type == RADIUS_ACCT_INTERIM) { - /* Remove any pending interim acct update for the same STA. */ - radius_client_list_del(radius, msg_type, addr); - } - if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { if (conf->acct_server && radius->acct_sock < 0) radius_client_init_acct(radius); @@ -1015,6 +1050,9 @@ radius_change_server(struct radius_client_data *radius, int sel_sock; struct radius_msg_list *entry; struct hostapd_radius_servers *conf = radius->conf; + struct sockaddr_in disconnect_addr = { + .sin_family = AF_UNSPEC, + }; hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, @@ -1023,6 +1061,12 @@ radius_change_server(struct radius_client_data *radius, hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), nserv->port); + if (oserv && oserv == nserv) { + /* Reconnect to same server, flush */ + if (auth) + radius_client_flush(radius, 1); + } + if (oserv && oserv != nserv && (nserv->shared_secret_len != oserv->shared_secret_len || os_memcmp(nserv->shared_secret, oserv->shared_secret, @@ -1125,6 +1169,11 @@ radius_change_server(struct radius_client_data *radius, } } + /* Force a reconnect by disconnecting the socket first */ + if (connect(sel_sock, (struct sockaddr *) &disconnect_addr, + sizeof(disconnect_addr)) < 0) + wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno)); + if (connect(sel_sock, addr, addrlen) < 0) { wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno)); return -1; @@ -1587,11 +1636,16 @@ static int radius_client_dump_acct_server(char *buf, size_t buflen, int radius_client_get_mib(struct radius_client_data *radius, char *buf, size_t buflen) { - struct hostapd_radius_servers *conf = radius->conf; + struct hostapd_radius_servers *conf; int i; struct hostapd_radius_server *serv; int count = 0; + if (!radius) + return 0; + + conf = radius->conf; + if (conf->auth_servers) { for (i = 0; i < conf->num_auth_servers; i++) { serv = &conf->auth_servers[i]; diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h index 3db16aa282ba7..8ca0874db498a 100644 --- a/src/radius/radius_client.h +++ b/src/radius/radius_client.h @@ -241,6 +241,9 @@ int radius_client_register(struct radius_client_data *radius, const u8 *shared_secret, size_t shared_secret_len, void *data), void *data); +void radius_client_set_interim_error_cb(struct radius_client_data *radius, + void (*cb)(const u8 *addr, void *ctx), + void *ctx); int radius_client_send(struct radius_client_data *radius, struct radius_msg *msg, RadiusType msg_type, const u8 *addr); diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c index b7d991bbd0979..8a3d7e0324bc2 100644 --- a/src/radius/radius_das.c +++ b/src/radius/radius_das.c @@ -23,6 +23,7 @@ struct radius_das_data { struct hostapd_ip_addr client_addr; unsigned int time_window; int require_event_timestamp; + int require_message_authenticator; void *ctx; enum radius_das_res (*disconnect)(void *ctx, struct radius_das_attrs *attr); @@ -234,9 +235,11 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) radius_msg_dump(msg); if (radius_msg_verify_das_req(msg, das->shared_secret, - das->shared_secret_len)) { - wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet " - "from %s:%d - drop", abuf, from_port); + das->shared_secret_len, + das->require_message_authenticator)) { + wpa_printf(MSG_DEBUG, + "DAS: Invalid authenticator or Message-Authenticator in packet from %s:%d - drop", + abuf, from_port); goto fail; } @@ -362,6 +365,8 @@ radius_das_init(struct radius_das_conf *conf) das->time_window = conf->time_window; das->require_event_timestamp = conf->require_event_timestamp; + das->require_message_authenticator = + conf->require_message_authenticator; das->ctx = conf->ctx; das->disconnect = conf->disconnect; diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h index ce731d46a9ace..9863fdc1eaca0 100644 --- a/src/radius/radius_das.h +++ b/src/radius/radius_das.h @@ -44,6 +44,7 @@ struct radius_das_conf { const struct hostapd_ip_addr *client_addr; unsigned int time_window; int require_event_timestamp; + int require_message_authenticator; void *ctx; enum radius_das_res (*disconnect)(void *ctx, struct radius_das_attrs *attr); diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index ef7b683864765..3d8d12223c261 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -15,7 +15,7 @@ #include "wpa_i.h" #include "pmksa_cache.h" -#ifdef IEEE8021X_EAPOL +#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA) static const int pmksa_cache_max_entries = 32; @@ -109,6 +109,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @pmk: The new pairwise master key * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @pmkid: Calculated PMKID * @kck: Key confirmation key or %NULL if not yet derived * @kck_len: KCK length in bytes * @aa: Authenticator address @@ -124,13 +125,13 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) */ struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, - const u8 *kck, size_t kck_len, + const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp) { struct rsn_pmksa_cache_entry *entry, *pos, *prev; struct os_reltime now; - if (pmk_len > PMK_LEN) + if (pmk_len > PMK_LEN_MAX) return NULL; if (wpa_key_mgmt_suite_b(akmp) && !kck) @@ -141,7 +142,9 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, return NULL; os_memcpy(entry->pmk, pmk, pmk_len); entry->pmk_len = pmk_len; - if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + if (pmkid) + os_memcpy(entry->pmkid, pmkid, PMKID_LEN); + else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid); else if (wpa_key_mgmt_suite_b(akmp)) rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); @@ -344,7 +347,7 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *new_entry; new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len, - NULL, 0, + NULL, NULL, 0, aa, pmksa->sm->own_addr, old_entry->network_ctx, old_entry->akmp); if (new_entry == NULL) diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h index f8e040e067d95..daede6dac7fe6 100644 --- a/src/rsn_supp/pmksa_cache.h +++ b/src/rsn_supp/pmksa_cache.h @@ -15,7 +15,7 @@ struct rsn_pmksa_cache_entry { struct rsn_pmksa_cache_entry *next; u8 pmkid[PMKID_LEN]; - u8 pmk[PMK_LEN]; + u8 pmk[PMK_LEN_MAX]; size_t pmk_len; os_time_t expiration; int akmp; /* WPA_KEY_MGMT_* */ @@ -44,7 +44,7 @@ enum pmksa_free_reason { PMKSA_EXPIRE, }; -#ifdef IEEE8021X_EAPOL +#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA) struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, @@ -57,7 +57,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, - const u8 *kck, size_t kck_len, + const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp); struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm); void pmksa_cache_clear_current(struct wpa_sm *sm); @@ -105,7 +105,7 @@ static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, static inline struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, - const u8 *kck, size_t kck_len, + const u8 *pmkid, const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp) { return NULL; diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c index c6534af2c96eb..4c9a4fb8b14ca 100644 --- a/src/rsn_supp/preauth.c +++ b/src/rsn_supp/preauth.c @@ -18,7 +18,7 @@ #include "wpa_i.h" -#ifdef IEEE8021X_EAPOL +#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA) #define PMKID_CANDIDATE_PRIO_SCAN 1000 @@ -93,7 +93,7 @@ static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth", pmk, pmk_len); sm->pmk_len = pmk_len; - pmksa_cache_add(sm->pmksa, pmk, pmk_len, + pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL, NULL, 0, sm->preauth_bssid, sm->own_addr, sm->network_ctx, @@ -538,4 +538,4 @@ int rsn_preauth_in_progress(struct wpa_sm *sm) return sm->preauth_eapol != NULL; } -#endif /* IEEE8021X_EAPOL */ +#endif /* IEEE8021X_EAPOL && !CONFIG_NO_WPA */ diff --git a/src/rsn_supp/preauth.h b/src/rsn_supp/preauth.h index 277f0663b0f08..8caf3ee56b5f2 100644 --- a/src/rsn_supp/preauth.h +++ b/src/rsn_supp/preauth.h @@ -11,7 +11,7 @@ struct wpa_scan_results; -#ifdef IEEE8021X_EAPOL +#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA) void pmksa_candidate_free(struct wpa_sm *sm); int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, @@ -27,7 +27,7 @@ int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose); int rsn_preauth_in_progress(struct wpa_sm *sm); -#else /* IEEE8021X_EAPOL */ +#else /* IEEE8021X_EAPOL && !CONFIG_NO_WPA */ static inline void pmksa_candidate_free(struct wpa_sm *sm) { @@ -74,6 +74,6 @@ static inline int rsn_preauth_in_progress(struct wpa_sm *sm) return 0; } -#endif /* IEEE8021X_EAPOL */ +#endif /* IEEE8021X_EAPOL && !CONFIG_NO_WPA */ #endif /* PREAUTH_H */ diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c index 722c20a706f9d..e4241681842ac 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -627,9 +627,15 @@ static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx) */ if (peer->initiator) { + u8 addr[ETH_ALEN]; + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR " - try to renew", MAC2STR(peer->addr)); - wpa_tdls_start(sm, peer->addr); + /* cache the peer address before do_teardown */ + os_memcpy(addr, peer->addr, ETH_ALEN); + wpa_tdls_do_teardown(sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + wpa_tdls_start(sm, addr); } else { wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR " - tear down", MAC2STR(peer->addr)); @@ -2170,6 +2176,14 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, "ignore TPK M2 from " MACSTR, MAC2STR(src_addr)); return -1; } + + if (peer->tpk_success) { + wpa_printf(MSG_INFO, "TDLS: Ignore incoming TPK M2 retry, from " + MACSTR " as TPK M3 was already sent", + MAC2STR(src_addr)); + return 0; + } + wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST); if (len < 3 + 2 + 1) { @@ -2325,7 +2339,7 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, kde.ftie, sizeof(*ftie)); ftie = (struct wpa_tdls_ftie *) kde.ftie; - if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { + if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does " "not match with FTIE SNonce used in TPK M1"); /* Silently discard the frame */ @@ -2386,7 +2400,7 @@ skip_rsn: wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / " "TPK Handshake Message 3"); if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0) - goto error; + goto error_no_msg; if (!peer->tpk_success) { /* @@ -2407,6 +2421,7 @@ skip_rsn: error: wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1, status); +error_no_msg: wpa_tdls_disable_peer_link(sm, peer); return -1; } @@ -2503,13 +2518,13 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, goto error; } - if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) { + if (os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does " "not match with FTIE ANonce used in TPK M2"); goto error; } - if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { + if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not " "match with FTIE SNonce used in TPK M1"); goto error; diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index a9f255e37b436..3c4787925e20a 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -1,6 +1,7 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * Copyright(c) 2015 Intel Deutschland GmbH * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,6 +24,9 @@ #include "peerkey.h" +static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + /** * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -34,11 +38,13 @@ * @msg: EAPOL-Key message * @msg_len: Length of message * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written + * Returns: >= 0 on success, < 0 on failure */ -void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, - int ver, const u8 *dest, u16 proto, - u8 *msg, size_t msg_len, u8 *key_mic) +int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, + int ver, const u8 *dest, u16 proto, + u8 *msg, size_t msg_len, u8 *key_mic) { + int ret = -1; size_t mic_len = wpa_mic_len(sm->key_mgmt); if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) { @@ -69,10 +75,11 @@ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len); wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len); wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len); - wpa_sm_ether_send(sm, dest, proto, msg, msg_len); + ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len); eapol_sm_notify_tx_eapol_key(sm->eapol); out: os_free(msg); + return ret; } @@ -124,7 +131,7 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info = WPA_KEY_INFO_REQUEST | ver; if (sm->ptk_set) - key_info |= WPA_KEY_INFO_MIC; + key_info |= WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; if (error) key_info |= WPA_KEY_INFO_ERROR; if (pairwise) @@ -206,15 +213,21 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, #endif /* CONFIG_IEEE80211R */ } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) { int res, pmk_len; - pmk_len = PMK_LEN; - res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN); + + if (sm->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + pmk_len = PMK_LEN_SUITE_B_192; + else + pmk_len = PMK_LEN; + res = eapol_sm_get_key(sm->eapol, sm->pmk, pmk_len); if (res) { - /* - * EAP-LEAP is an exception from other EAP methods: it - * uses only 16-byte PMK. - */ - res = eapol_sm_get_key(sm->eapol, sm->pmk, 16); - pmk_len = 16; + if (pmk_len == PMK_LEN) { + /* + * EAP-LEAP is an exception from other EAP + * methods: it uses only 16-byte PMK. + */ + res = eapol_sm_get_key(sm->eapol, sm->pmk, 16); + pmk_len = 16; + } } else { #ifdef CONFIG_IEEE80211R u8 buf[2 * PMK_LEN]; @@ -236,7 +249,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, !wpa_key_mgmt_suite_b(sm->key_mgmt) && !wpa_key_mgmt_ft(sm->key_mgmt)) { sa = pmksa_cache_add(sm->pmksa, - sm->pmk, pmk_len, + sm->pmk, pmk_len, NULL, NULL, 0, src_addr, sm->own_addr, sm->network_ctx, @@ -257,7 +270,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, * much we can do here without knowing what * exactly caused the server to misbehave. */ - wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: PMKID mismatch - authentication server may have derived different MSK?!"); return -1; } @@ -318,7 +331,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, * @wpa_ie: WPA/RSN IE * @wpa_ie_len: Length of the WPA/RSN IE * @ptk: PTK to use for keyed hash and encryption - * Returns: 0 on success, -1 on failure + * Returns: >= 0 on success, < 0 on failure */ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, @@ -351,13 +364,12 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, if (rsn_ie_buf == NULL) return -1; os_memcpy(rsn_ie_buf, wpa_ie, wpa_ie_len); - res = wpa_insert_pmkid(rsn_ie_buf, wpa_ie_len, + res = wpa_insert_pmkid(rsn_ie_buf, &wpa_ie_len, sm->pmk_r1_name); if (res < 0) { os_free(rsn_ie_buf); return -1; } - wpa_ie_len += res; if (sm->assoc_resp_ies) { os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies, @@ -409,10 +421,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); - wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL, - rbuf, rlen, key_mic); - - return 0; + return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, + ETH_P_EAPOL, rbuf, rlen, key_mic); } @@ -500,6 +510,7 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, os_memset(buf, 0, sizeof(buf)); } sm->tptk_set = 1; + sm->tk_to_set = 1; kde = sm->assoc_wpa_ie; kde_len = sm->assoc_wpa_ie_len; @@ -525,7 +536,7 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, #endif /* CONFIG_P2P */ if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, - kde, kde_len, ptk)) + kde, kde_len, ptk) < 0) goto failed; os_free(kde_buf); @@ -603,7 +614,12 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, int keylen, rsclen; enum wpa_alg alg; const u8 *key_rsc; - u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + if (!sm->tk_to_set) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Do not re-install same PTK to the driver"); + return 0; + } wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Installing PTK to the driver"); @@ -643,6 +659,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, /* TK is not needed anymore in supplicant */ os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); + sm->tk_to_set = 0; if (sm->wpa_ptk_rekey) { eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); @@ -753,12 +770,43 @@ static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, } +static int wpa_supplicant_rsc_relaxation(const struct wpa_sm *sm, + const u8 *rsc) +{ + int rsclen; + + if (!sm->wpa_rsc_relaxation) + return 0; + + rsclen = wpa_cipher_rsc_len(sm->group_cipher); + + /* + * Try to detect RSC (endian) corruption issue where the AP sends + * the RSC bytes in EAPOL-Key message in the wrong order, both if + * it's actually a 6-byte field (as it should be) and if it treats + * it as an 8-byte field. + * An AP model known to have this bug is the Sapido RB-1632. + */ + if (rsclen == 6 && ((rsc[5] && !rsc[0]) || rsc[6] || rsc[7])) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "RSC %02x%02x%02x%02x%02x%02x%02x%02x is likely bogus, using 0", + rsc[0], rsc[1], rsc[2], rsc[3], + rsc[4], rsc[5], rsc[6], rsc[7]); + + return 1; + } + + return 0; +} + + static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, const struct wpa_eapol_key *key, const u8 *gtk, size_t gtk_len, int key_info) { struct wpa_gtk_data gd; + const u8 *key_rsc; /* * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x @@ -784,11 +832,15 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, os_memcpy(gd.gtk, gtk, gtk_len); gd.gtk_len = gtk_len; + key_rsc = key->key_rsc; + if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc)) + key_rsc = null_rsc; + if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED && (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gtk_len, gtk_len, &gd.key_rsc_len, &gd.alg) || - wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) { + wpa_supplicant_install_gtk(sm, &gd, key_rsc))) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Failed to install GTK"); os_memset(&gd, 0, sizeof(gd)); @@ -989,8 +1041,8 @@ static int wpa_supplicant_validate_ie_ft(struct wpa_sm *sm, if (sm->assoc_resp_ies) { pos = sm->assoc_resp_ies; end = pos + sm->assoc_resp_ies_len; - while (pos + 2 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 2) { + if (2 + pos[1] > end - pos) break; switch (*pos) { case WLAN_EID_MOBILITY_DOMAIN: @@ -1086,7 +1138,7 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, * @ver: Version bits from EAPOL-Key Key Info * @key_info: Key Info * @ptk: PTK to use for keyed hash and encryption - * Returns: 0 on success, -1 on failure + * Returns: >= 0 on success, < 0 on failure */ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, @@ -1126,10 +1178,8 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, WPA_PUT_BE16(reply->key_data_length, 0); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); - wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL, - rbuf, rlen, key_mic); - - return 0; + return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, + ETH_P_EAPOL, rbuf, rlen, key_mic); } @@ -1202,7 +1252,7 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, #endif /* CONFIG_P2P */ if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, - &sm->ptk)) { + &sm->ptk) < 0) { goto failed; } @@ -1247,7 +1297,7 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) { struct rsn_pmksa_cache_entry *sa; - sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, + sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL, sm->ptk.kck, sm->ptk.kck_len, sm->bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt); @@ -1437,10 +1487,8 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, WPA_PUT_BE16(reply->key_data_length, 0); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); - wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid, - ETH_P_EAPOL, rbuf, rlen, key_mic); - - return 0; + return wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, + sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic); } @@ -1453,6 +1501,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, u16 key_info; int rekey, ret; struct wpa_gtk_data gd; + const u8 *key_rsc; if (!sm->msg_3_of_4_ok) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, @@ -1483,8 +1532,12 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, if (ret) goto failed; - if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) || - wpa_supplicant_send_2_of_2(sm, key, ver, key_info)) + key_rsc = key->key_rsc; + if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc)) + key_rsc = null_rsc; + + if (wpa_supplicant_install_gtk(sm, &gd, key_rsc) || + wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0) goto failed; os_memset(&gd, 0, sizeof(gd)); @@ -1617,14 +1670,14 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, } if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8, key_data, buf)) { - os_free(buf); + 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; } os_memcpy(key_data, buf, *key_data_len); - os_free(buf); + bin_clear_free(buf, *key_data_len); WPA_PUT_BE16(key->key_data_length, *key_data_len); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -2237,6 +2290,9 @@ void wpa_sm_deinit(struct wpa_sm *sm) #ifdef CONFIG_IEEE80211R os_free(sm->assoc_resp_ies); #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_TESTING_OPTIONS + wpabuf_free(sm->test_assoc_ie); +#endif /* CONFIG_TESTING_OPTIONS */ os_free(sm); } @@ -2335,12 +2391,13 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) * @sm: Pointer to WPA state machine data from wpa_sm_init() * @pmk: The new PMK * @pmk_len: The length of the new PMK in bytes + * @pmkid: Calculated PMKID * @bssid: AA to add into PMKSA cache or %NULL to not cache the PMK * * Configure the PMK for WPA state machine. */ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, - const u8 *bssid) + const u8 *pmkid, const u8 *bssid) { if (sm == NULL) return; @@ -2355,7 +2412,7 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, #endif /* CONFIG_IEEE80211R */ if (bssid) { - pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL, 0, + pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0, bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt); } @@ -2439,6 +2496,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->ssid_len = 0; sm->wpa_ptk_rekey = config->wpa_ptk_rekey; sm->p2p = config->p2p; + sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation; } else { sm->network_ctx = NULL; sm->peerkey_enabled = 0; @@ -2449,6 +2507,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->ssid_len = 0; sm->wpa_ptk_rekey = 0; sm->p2p = 0; + sm->wpa_rsc_relaxation = 0; } } @@ -2636,6 +2695,17 @@ int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, if (sm == NULL) return -1; +#ifdef CONFIG_TESTING_OPTIONS + if (sm->test_assoc_ie) { + wpa_printf(MSG_DEBUG, + "TESTING: Replace association WPA/RSN IE"); + if (*wpa_ie_len < wpabuf_len(sm->test_assoc_ie)) + return -1; + os_memcpy(wpa_ie, wpabuf_head(sm->test_assoc_ie), + wpabuf_len(sm->test_assoc_ie)); + res = wpabuf_len(sm->test_assoc_ie); + } else +#endif /* CONFIG_TESTING_OPTIONS */ res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len); if (res < 0) return -1; @@ -2975,3 +3045,12 @@ void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, } sm->ptk_set = 1; } + + +#ifdef CONFIG_TESTING_OPTIONS +void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf) +{ + wpabuf_free(sm->test_assoc_ie); + sm->test_assoc_ie = buf; +} +#endif /* CONFIG_TESTING_OPTIONS */ diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index e163b7010b70e..0b7477f31bc7a 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -104,6 +104,7 @@ struct rsn_supp_config { size_t ssid_len; int wpa_ptk_rekey; int p2p; + int wpa_rsc_relaxation; }; #ifndef CONFIG_NO_WPA @@ -113,7 +114,7 @@ void wpa_sm_deinit(struct wpa_sm *sm); void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid); void wpa_sm_notify_disassoc(struct wpa_sm *sm); void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, - const u8 *bssid); + const u8 *pmkid, const u8 *bssid); void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm); void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth); void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx); @@ -180,7 +181,8 @@ static inline void wpa_sm_notify_disassoc(struct wpa_sm *sm) } static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, - size_t pmk_len) + size_t pmk_len, const u8 *pmkid, + const u8 *bssid) { } @@ -320,7 +322,8 @@ static inline void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, } static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, - const u8 *ptk_kek) + size_t ptk_kck_len, + const u8 *ptk_kek, size_t ptk_kek_len) { } @@ -415,7 +418,12 @@ int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr, u8 oper_class, struct hostapd_freq_params *freq_params); int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr); +#ifdef CONFIG_TDLS_TESTING +extern unsigned int tdls_testing; +#endif /* CONFIG_TDLS_TESTING */ + int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf); +void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf); #endif /* WPA_H */ diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 965a9c1d577c3..f653ba6e041d4 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -19,11 +19,12 @@ struct wpa_eapol_key; * struct wpa_sm - Internal WPA state machine data */ struct wpa_sm { - u8 pmk[PMK_LEN]; + u8 pmk[PMK_LEN_MAX]; size_t pmk_len; struct wpa_ptk ptk, tptk; int ptk_set, tptk_set; unsigned int msg_3_of_4_ok:1; + unsigned int tk_to_set:1; u8 snonce[WPA_NONCE_LEN]; u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */ int renew_snonce; @@ -60,6 +61,7 @@ struct wpa_sm { size_t ssid_len; int wpa_ptk_rekey; int p2p; + int wpa_rsc_relaxation; u8 own_addr[ETH_ALEN]; const char *ifname; @@ -132,6 +134,10 @@ struct wpa_sm { #ifdef CONFIG_P2P u8 p2p_ip_addr[3 * 4]; #endif /* CONFIG_P2P */ + +#ifdef CONFIG_TESTING_OPTIONS + struct wpabuf *test_assoc_ie; +#endif /* CONFIG_TESTING_OPTIONS */ }; @@ -342,16 +348,14 @@ wpa_sm_tdls_disable_channel_switch(struct wpa_sm *sm, const u8 *addr) static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len) { - if (!sm->proactive_key_caching) - return 0; if (!sm->ctx->key_mgmt_set_pmk) return -1; return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len); } -void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, - int ver, const u8 *dest, u16 proto, - u8 *msg, size_t msg_len, u8 *key_mic); +int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, + int ver, const u8 *dest, u16 proto, + u8 *msg, size_t msg_len, u8 *key_mic); int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, int ver, const u8 *nonce, diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index 0c37b35c1ee1d..c44844ec583bf 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -378,7 +378,7 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, return 0; } - if (pos + 1 + RSN_SELECTOR_LEN < end && + if (1 + RSN_SELECTOR_LEN < end - pos && pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; @@ -491,13 +491,13 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, int ret = 0; os_memset(ie, 0, sizeof(*ie)); - for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) { if (pos[0] == 0xdd && ((pos == buf + len - 1) || pos[1] == 0)) { /* Ignore padding */ break; } - if (pos + 2 + pos[1] > end) { + if (2 + pos[1] > end - pos) { wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " "underflow (ie=%d len=%d pos=%d)", pos[0], pos[1], (int) (pos - buf)); diff --git a/src/tls/Makefile b/src/tls/Makefile index 27cdfcaa97489..52a890a157d2e 100644 --- a/src/tls/Makefile +++ b/src/tls/Makefile @@ -24,6 +24,7 @@ LIB_OBJS= \ tlsv1_client.o \ tlsv1_client_read.o \ tlsv1_client_write.o \ + tlsv1_client_ocsp.o \ tlsv1_common.o \ tlsv1_cred.o \ tlsv1_record.o \ diff --git a/src/tls/asn1.h b/src/tls/asn1.h index 74750076731de..6bd7df565dba4 100644 --- a/src/tls/asn1.h +++ b/src/tls/asn1.h @@ -20,6 +20,7 @@ #define ASN1_TAG_EXTERNAL 0x08 /* not yet parsed */ #define ASN1_TAG_REAL 0x09 /* not yet parsed */ #define ASN1_TAG_ENUMERATED 0x0A /* not yet parsed */ +#define ASN1_TAG_EMBEDDED_PDV 0x0B /* not yet parsed */ #define ASN1_TAG_UTF8STRING 0x0C /* not yet parsed */ #define ANS1_TAG_RELATIVE_OID 0x0D #define ASN1_TAG_SEQUENCE 0x10 /* shall be constructed */ @@ -35,7 +36,8 @@ #define ASN1_TAG_VISIBLESTRING 0x1A #define ASN1_TAG_GENERALSTRING 0x1B /* not yet parsed */ #define ASN1_TAG_UNIVERSALSTRING 0x1C /* not yet parsed */ -#define ASN1_TAG_BMPSTRING 0x1D /* not yet parsed */ +#define ASN1_TAG_CHARACTERSTRING 0x1D /* not yet parsed */ +#define ASN1_TAG_BMPSTRING 0x1E /* not yet parsed */ #define ASN1_CLASS_UNIVERSAL 0 #define ASN1_CLASS_APPLICATION 1 diff --git a/src/tls/pkcs5.c b/src/tls/pkcs5.c index 8a93483781d80..a2ad83b8a898d 100644 --- a/src/tls/pkcs5.c +++ b/src/tls/pkcs5.c @@ -1,6 +1,6 @@ /* * PKCS #5 (Password-based Encryption) - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,7 @@ #include "common.h" #include "crypto/crypto.h" #include "crypto/md5.h" +#include "crypto/sha1.h" #include "asn1.h" #include "pkcs5.h" @@ -18,30 +19,261 @@ struct pkcs5_params { enum pkcs5_alg { PKCS5_ALG_UNKNOWN, - PKCS5_ALG_MD5_DES_CBC + PKCS5_ALG_MD5_DES_CBC, + PKCS5_ALG_PBES2, + PKCS5_ALG_SHA1_3DES_CBC, } alg; - u8 salt[8]; + u8 salt[64]; size_t salt_len; unsigned int iter_count; + enum pbes2_enc_alg { + PBES2_ENC_ALG_UNKNOWN, + PBES2_ENC_ALG_DES_EDE3_CBC, + } enc_alg; + u8 iv[8]; + size_t iv_len; }; +static int oid_is_rsadsi(struct asn1_oid *oid) +{ + return oid->len >= 4 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */; +} + + +static int pkcs5_is_oid(struct asn1_oid *oid, unsigned long alg) +{ + return oid->len == 7 && + oid_is_rsadsi(oid) && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 5 /* pkcs-5 */ && + oid->oid[6] == alg; +} + + +static int enc_alg_is_oid(struct asn1_oid *oid, unsigned long alg) +{ + return oid->len == 6 && + oid_is_rsadsi(oid) && + oid->oid[4] == 3 /* encryptionAlgorithm */ && + oid->oid[5] == alg; +} + + +static int pkcs12_is_pbe_oid(struct asn1_oid *oid, unsigned long alg) +{ + return oid->len == 8 && + oid_is_rsadsi(oid) && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 12 /* pkcs-12 */ && + oid->oid[6] == 1 /* pkcs-12PbeIds */ && + oid->oid[7] == alg; +} + + static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid) { - if (oid->len == 7 && - oid->oid[0] == 1 /* iso */ && - oid->oid[1] == 2 /* member-body */ && - oid->oid[2] == 840 /* us */ && - oid->oid[3] == 113549 /* rsadsi */ && - oid->oid[4] == 1 /* pkcs */ && - oid->oid[5] == 5 /* pkcs-5 */ && - oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */) + if (pkcs5_is_oid(oid, 3)) /* pbeWithMD5AndDES-CBC (PBES1) */ return PKCS5_ALG_MD5_DES_CBC; - + if (pkcs12_is_pbe_oid(oid, 3)) /* pbeWithSHAAnd3-KeyTripleDES-CBC */ + return PKCS5_ALG_SHA1_3DES_CBC; + if (pkcs5_is_oid(oid, 13)) /* id-PBES2 (PBES2) */ + return PKCS5_ALG_PBES2; return PKCS5_ALG_UNKNOWN; } +static int pkcs5_get_params_pbes2(struct pkcs5_params *params, const u8 *pos, + const u8 *enc_alg_end) +{ + struct asn1_hdr hdr; + const u8 *end, *kdf_end; + struct asn1_oid oid; + char obuf[80]; + + /* + * RFC 2898, Ch. A.4 + * + * PBES2-params ::= SEQUENCE { + * keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}}, + * encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} } + * + * PBES2-KDFs ALGORITHM-IDENTIFIER ::= + * { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... } + */ + + if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected SEQUENCE (PBES2-params) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected SEQUENCE (keyDerivationFunc) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + kdf_end = end = hdr.payload + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Failed to parse OID (keyDerivationFunc algorithm)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 keyDerivationFunc algorithm %s", + obuf); + if (!pkcs5_is_oid(&oid, 12)) /* id-PBKDF2 */ { + wpa_printf(MSG_DEBUG, + "PKCS #5: Unsupported PBES2 keyDerivationFunc algorithm %s", + obuf); + return -1; + } + + /* + * RFC 2898, C. + * + * PBKDF2-params ::= SEQUENCE { + * salt CHOICE { + * specified OCTET STRING, + * otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}} + * }, + * iterationCount INTEGER (1..MAX), + * keyLength INTEGER (1..MAX) OPTIONAL, + * prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT + * algid-hmacWithSHA1 + * } + */ + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected SEQUENCE (PBKDF2-params) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* For now, only support the salt CHOICE specified (OCTET STRING) */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING || + hdr.length > sizeof(params->salt)) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected OCTET STRING (salt.specified) - found class %d tag 0x%x size %d", + hdr.class, hdr.tag, hdr.length); + return -1; + } + pos = hdr.payload + hdr.length; + os_memcpy(params->salt, hdr.payload, hdr.length); + params->salt_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "PKCS #5: salt", params->salt, params->salt_len); + + /* iterationCount INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected INTEGER - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length == 1) { + params->iter_count = *hdr.payload; + } else if (hdr.length == 2) { + params->iter_count = WPA_GET_BE16(hdr.payload); + } else if (hdr.length == 4) { + params->iter_count = WPA_GET_BE32(hdr.payload); + } else { + wpa_hexdump(MSG_DEBUG, + "PKCS #5: Unsupported INTEGER value (iterationCount)", + hdr.payload, hdr.length); + return -1; + } + wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x", + params->iter_count); + if (params->iter_count == 0 || params->iter_count > 0xffff) { + wpa_printf(MSG_INFO, "PKCS #5: Unsupported iterationCount=0x%x", + params->iter_count); + return -1; + } + + /* For now, ignore optional keyLength and prf */ + + pos = kdf_end; + + /* encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} */ + + if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected SEQUENCE (encryptionScheme) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Failed to parse OID (encryptionScheme algorithm)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 encryptionScheme algorithm %s", + obuf); + if (enc_alg_is_oid(&oid, 7)) { + params->enc_alg = PBES2_ENC_ALG_DES_EDE3_CBC; + } else { + wpa_printf(MSG_DEBUG, + "PKCS #5: Unsupported PBES2 encryptionScheme algorithm %s", + obuf); + return -1; + } + + /* + * RFC 2898, B.2.2: + * The parameters field associated with this OID in an + * AlgorithmIdentifier shall have type OCTET STRING (SIZE(8)), + * specifying the initialization vector for CBC mode. + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING || + hdr.length != 8) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected OCTET STRING (SIZE(8)) (IV) - found class %d tag 0x%x size %d", + hdr.class, hdr.tag, hdr.length); + return -1; + } + os_memcpy(params->iv, hdr.payload, hdr.length); + params->iv_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "PKCS #5: IV", params->iv, params->iv_len); + + return 0; +} + + static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, struct pkcs5_params *params) { @@ -71,11 +303,23 @@ static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, return -1; } + if (params->alg == PKCS5_ALG_PBES2) + return pkcs5_get_params_pbes2(params, pos, enc_alg_end); + + /* PBES1 */ + /* * PKCS#5, Section 8 * PBEParameter ::= SEQUENCE { * salt OCTET STRING SIZE(8), * iterationCount INTEGER } + * + * Note: The same implementation can be used to parse the PKCS #12 + * version described in RFC 7292, C: + * pkcs-12PbeParams ::= SEQUENCE { + * salt OCTET STRING, + * iterations INTEGER + * } */ if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 || @@ -89,11 +333,11 @@ static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, pos = hdr.payload; end = hdr.payload + hdr.length; - /* salt OCTET STRING SIZE(8) */ + /* salt OCTET STRING SIZE(8) (PKCS #5) or OCTET STRING (PKCS #12) */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OCTETSTRING || - hdr.length != 8) { + hdr.length > sizeof(params->salt)) { wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) " "(salt) - found class %d tag 0x%x size %d", hdr.class, hdr.tag, hdr.length); @@ -136,6 +380,174 @@ static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, } +static struct crypto_cipher * +pkcs5_crypto_init_pbes2(struct pkcs5_params *params, const char *passwd) +{ + u8 key[24]; + + if (params->enc_alg != PBES2_ENC_ALG_DES_EDE3_CBC || + params->iv_len != 8) + return NULL; + + wpa_hexdump_ascii_key(MSG_DEBUG, "PKCS #5: PBES2 password for PBKDF2", + passwd, os_strlen(passwd)); + wpa_hexdump(MSG_DEBUG, "PKCS #5: PBES2 salt for PBKDF2", + params->salt, params->salt_len); + wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 PBKDF2 iterations: %u", + params->iter_count); + if (pbkdf2_sha1(passwd, params->salt, params->salt_len, + params->iter_count, key, sizeof(key)) < 0) + return NULL; + wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES EDE3 key", key, sizeof(key)); + wpa_hexdump(MSG_DEBUG, "PKCS #5: DES IV", params->iv, params->iv_len); + + return crypto_cipher_init(CRYPTO_CIPHER_ALG_3DES, params->iv, + key, sizeof(key)); +} + + +static void add_byte_array_mod(u8 *a, const u8 *b, size_t len) +{ + size_t i; + unsigned int carry = 0; + + for (i = len - 1; i < len; i--) { + carry = carry + a[i] + b[i]; + a[i] = carry & 0xff; + carry >>= 8; + } +} + + +static int pkcs12_key_gen(const u8 *pw, size_t pw_len, const u8 *salt, + size_t salt_len, u8 id, unsigned int iter, + size_t out_len, u8 *out) +{ + unsigned int u, v, S_len, P_len, i; + u8 *D = NULL, *I = NULL, *B = NULL, *pos; + int res = -1; + + /* RFC 7292, B.2 */ + u = SHA1_MAC_LEN; + v = 64; + + /* D = copies of ID */ + D = os_malloc(v); + if (!D) + goto done; + os_memset(D, id, v); + + /* S = copies of salt; P = copies of password, I = S || P */ + S_len = v * ((salt_len + v - 1) / v); + P_len = v * ((pw_len + v - 1) / v); + I = os_malloc(S_len + P_len); + if (!I) + goto done; + pos = I; + if (salt_len) { + for (i = 0; i < S_len; i++) + *pos++ = salt[i % salt_len]; + } + if (pw_len) { + for (i = 0; i < P_len; i++) + *pos++ = pw[i % pw_len]; + } + + B = os_malloc(v); + if (!B) + goto done; + + for (;;) { + u8 hash[SHA1_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + + addr[0] = D; + len[0] = v; + addr[1] = I; + len[1] = S_len + P_len; + if (sha1_vector(2, addr, len, hash) < 0) + goto done; + + addr[0] = hash; + len[0] = SHA1_MAC_LEN; + for (i = 1; i < iter; i++) { + if (sha1_vector(1, addr, len, hash) < 0) + goto done; + } + + if (out_len <= u) { + os_memcpy(out, hash, out_len); + res = 0; + goto done; + } + + os_memcpy(out, hash, u); + out += u; + out_len -= u; + + /* I_j = (I_j + B + 1) mod 2^(v*8) */ + /* B = copies of Ai (final hash value) */ + for (i = 0; i < v; i++) + B[i] = hash[i % u]; + inc_byte_array(B, v); + for (i = 0; i < S_len + P_len; i += v) + add_byte_array_mod(&I[i], B, v); + } + +done: + os_free(B); + os_free(I); + os_free(D); + return res; +} + + +#define PKCS12_ID_ENC 1 +#define PKCS12_ID_IV 2 +#define PKCS12_ID_MAC 3 + +static struct crypto_cipher * +pkcs12_crypto_init_sha1(struct pkcs5_params *params, const char *passwd) +{ + unsigned int i; + u8 *pw; + size_t pw_len; + u8 key[24]; + u8 iv[8]; + + if (params->alg != PKCS5_ALG_SHA1_3DES_CBC) + return NULL; + + pw_len = passwd ? os_strlen(passwd) : 0; + pw = os_malloc(2 * (pw_len + 1)); + if (!pw) + return NULL; + if (pw_len) { + for (i = 0; i <= pw_len; i++) + WPA_PUT_BE16(&pw[2 * i], passwd[i]); + pw_len = 2 * (pw_len + 1); + } + + if (pkcs12_key_gen(pw, pw_len, params->salt, params->salt_len, + PKCS12_ID_ENC, params->iter_count, + sizeof(key), key) < 0 || + pkcs12_key_gen(pw, pw_len, params->salt, params->salt_len, + PKCS12_ID_IV, params->iter_count, + sizeof(iv), iv) < 0) { + os_free(pw); + return NULL; + } + + os_free(pw); + + wpa_hexdump_key(MSG_DEBUG, "PKCS #12: DES key", key, sizeof(key)); + wpa_hexdump_key(MSG_DEBUG, "PKCS #12: DES IV", iv, sizeof(iv)); + + return crypto_cipher_init(CRYPTO_CIPHER_ALG_3DES, iv, key, sizeof(key)); +} + + static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params, const char *passwd) { @@ -144,6 +556,12 @@ static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params, const u8 *addr[2]; size_t len[2]; + if (params->alg == PKCS5_ALG_PBES2) + return pkcs5_crypto_init_pbes2(params, passwd); + + if (params->alg == PKCS5_ALG_SHA1_3DES_CBC) + return pkcs12_crypto_init_sha1(params, passwd); + if (params->alg != PKCS5_ALG_MD5_DES_CBC) return NULL; diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c index a6f0587e34c57..9bc0d211f48dd 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-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,7 @@ #include "common.h" #include "crypto/sha1.h" #include "crypto/tls.h" +#include "x509v3.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_client.h" @@ -110,7 +111,6 @@ int tls_derive_keys(struct tlsv1_client *conn, pos += conn->rl.iv_size; /* server_write_IV */ os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); - pos += conn->rl.iv_size; } else { /* * Use IV field to set the mask value for TLS v1.1. A fixed @@ -494,6 +494,7 @@ void tlsv1_client_deinit(struct tlsv1_client *conn) tlsv1_client_free_dh(conn); tlsv1_cred_free(conn->cred); wpabuf_free(conn->partial_input); + x509_certificate_chain_free(conn->server_cert); os_free(conn); } @@ -691,18 +692,16 @@ int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, if (data == NULL || data_len == 0) return 0; - pos = conn->client_hello_ext = os_malloc(6 + data_len); + pos = conn->client_hello_ext = os_malloc(4 + data_len); if (pos == NULL) return -1; - WPA_PUT_BE16(pos, 4 + data_len); - pos += 2; WPA_PUT_BE16(pos, ext_type); pos += 2; WPA_PUT_BE16(pos, data_len); pos += 2; os_memcpy(pos, data, data_len); - conn->client_hello_ext_len = 6 + data_len; + conn->client_hello_ext_len = 4 + data_len; if (ext_type == TLS_EXT_PAC_OPAQUE) { conn->session_ticket_included = 1; @@ -813,9 +812,14 @@ int tlsv1_client_set_cred(struct tlsv1_client *conn, } -void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled) +/** + * tlsv1_client_set_flags - Set connection flags + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @flags: TLS_CONN_* bitfield + */ +void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags) { - conn->disable_time_checks = !enabled; + conn->flags = flags; } @@ -828,3 +832,38 @@ void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, conn->session_ticket_cb = cb; conn->session_ticket_cb_ctx = ctx; } + + +void tlsv1_client_set_cb(struct tlsv1_client *conn, + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data), + void *cb_ctx, + int cert_in_cb) +{ + conn->event_cb = event_cb; + conn->cb_ctx = cb_ctx; + conn->cert_in_cb = !!cert_in_cb; +} + + +int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf, + size_t buflen) +{ + if (!conn) + return -1; + switch (conn->rl.tls_version) { + case TLS_VERSION_1: + os_strlcpy(buf, "TLSv1", buflen); + break; + case TLS_VERSION_1_1: + os_strlcpy(buf, "TLSv1.1", buflen); + break; + case TLS_VERSION_1_2: + os_strlcpy(buf, "TLSv1.2", buflen); + break; + default: + return -1; + } + + return 0; +} diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h index a4e25e969937c..40fa6c7fbdeeb 100644 --- a/src/tls/tlsv1_client.h +++ b/src/tls/tlsv1_client.h @@ -41,7 +41,7 @@ int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn); int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers); int tlsv1_client_set_cred(struct tlsv1_client *conn, struct tlsv1_credentials *cred); -void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled); +void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags); typedef int (*tlsv1_client_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, @@ -51,4 +51,12 @@ void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, tlsv1_client_session_ticket_cb cb, void *ctx); +void tlsv1_client_set_cb(struct tlsv1_client *conn, + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data), + void *cb_ctx, + int cert_in_cb); +int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf, + size_t buflen); + #endif /* TLSV1_CLIENT_H */ diff --git a/src/tls/tlsv1_client_i.h b/src/tls/tlsv1_client_i.h index 55fdcf8d0435c..12ec8df6c3ac1 100644 --- a/src/tls/tlsv1_client_i.h +++ b/src/tls/tlsv1_client_i.h @@ -29,11 +29,14 @@ struct tlsv1_client { u8 alert_level; u8 alert_description; + unsigned int flags; /* TLS_CONN_* bitfield */ + unsigned int certificate_requested:1; unsigned int session_resumed:1; unsigned int session_ticket_included:1; unsigned int use_session_ticket:1; - unsigned int disable_time_checks:1; + unsigned int cert_in_cb:1; + unsigned int ocsp_resp_received:1; struct crypto_public_key *server_rsa_key; @@ -64,6 +67,12 @@ struct tlsv1_client { void *session_ticket_cb_ctx; struct wpabuf *partial_input; + + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; + + struct x509_certificate *server_cert; }; @@ -81,4 +90,11 @@ int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, const u8 *buf, size_t *len, u8 **out_data, size_t *out_len); +enum tls_ocsp_result { + TLS_OCSP_NO_RESPONSE, TLS_OCSP_INVALID, TLS_OCSP_GOOD, TLS_OCSP_REVOKED +}; + +enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn, + const u8 *resp, size_t len); + #endif /* TLSV1_CLIENT_I_H */ diff --git a/src/tls/tlsv1_client_ocsp.c b/src/tls/tlsv1_client_ocsp.c new file mode 100644 index 0000000000000..1d7b68ca286ed --- /dev/null +++ b/src/tls/tlsv1_client_ocsp.c @@ -0,0 +1,803 @@ +/* + * TLSv1 client - OCSP + * Copyright (c) 2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/tls.h" +#include "crypto/sha1.h" +#include "asn1.h" +#include "x509v3.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_client.h" +#include "tlsv1_client_i.h" + + +/* RFC 6960, 4.2.1: OCSPResponseStatus ::= ENUMERATED */ +enum ocsp_response_status { + OCSP_RESP_STATUS_SUCCESSFUL = 0, + OCSP_RESP_STATUS_MALFORMED_REQ = 1, + OCSP_RESP_STATUS_INT_ERROR = 2, + OCSP_RESP_STATUS_TRY_LATER = 3, + /* 4 not used */ + OCSP_RESP_STATUS_SIG_REQUIRED = 5, + OCSP_RESP_STATUS_UNAUTHORIZED = 6, +}; + + +static int is_oid_basic_ocsp_resp(struct asn1_oid *oid) +{ + return oid->len == 10 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 3 /* identified-organization */ && + oid->oid[2] == 6 /* dod */ && + oid->oid[3] == 1 /* internet */ && + oid->oid[4] == 5 /* security */ && + oid->oid[5] == 5 /* mechanisms */ && + oid->oid[6] == 7 /* id-pkix */ && + oid->oid[7] == 48 /* id-ad */ && + oid->oid[8] == 1 /* id-pkix-ocsp */ && + oid->oid[9] == 1 /* id-pkix-ocsp-basic */; +} + + +static int ocsp_responder_id_match(struct x509_certificate *signer, + struct x509_name *name, const u8 *key_hash) +{ + if (key_hash) { + u8 hash[SHA1_MAC_LEN]; + const u8 *addr[1] = { signer->public_key }; + size_t len[1] = { signer->public_key_len }; + + if (sha1_vector(1, addr, len, hash) < 0) + return 0; + return os_memcmp(hash, key_hash, SHA1_MAC_LEN) == 0; + } + + return x509_name_compare(&signer->subject, name) == 0; +} + + +static unsigned int ocsp_hash_data(struct asn1_oid *alg, const u8 *data, + size_t data_len, u8 *hash) +{ + const u8 *addr[1] = { data }; + size_t len[1] = { data_len }; + char buf[100]; + + if (x509_sha1_oid(alg)) { + if (sha1_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA1)", hash, 20); + return 20; + } + + if (x509_sha256_oid(alg)) { + if (sha256_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA256)", hash, 32); + return 32; + } + + if (x509_sha384_oid(alg)) { + if (sha384_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA384)", hash, 48); + return 48; + } + + if (x509_sha512_oid(alg)) { + if (sha512_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA512)", hash, 64); + return 64; + } + + + asn1_oid_to_str(alg, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "OCSP: Could not calculate hash with alg %s", + buf); + return 0; +} + + +static int tls_process_ocsp_single_response(struct tlsv1_client *conn, + struct x509_certificate *cert, + struct x509_certificate *issuer, + const u8 *resp, size_t len, + enum tls_ocsp_result *res) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + struct x509_algorithm_identifier alg; + const u8 *name_hash, *key_hash; + size_t name_hash_len, key_hash_len; + const u8 *serial_number; + size_t serial_number_len; + u8 hash[64]; + unsigned int hash_len; + unsigned int cert_status; + os_time_t update; + struct os_time now; + + wpa_hexdump(MSG_MSGDUMP, "OCSP: SingleResponse", resp, len); + + /* + * SingleResponse ::= SEQUENCE { + * certID CertID, + * certStatus CertStatus, + * thisUpdate GeneralizedTime, + * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + * singleExtensions [1] EXPLICIT Extensions OPTIONAL } + */ + + /* CertID ::= SEQUENCE */ + if (asn1_get_next(resp, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (CertID) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, + * issuerKeyHash OCTET STRING, + * serialNumber CertificateSerialNumber } + */ + + /* hashAlgorithm AlgorithmIdentifier */ + if (x509_parse_algorithm_identifier(pos, end - pos, &alg, &pos)) + return -1; + + /* issuerNameHash OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected OCTET STRING (issuerNameHash) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + name_hash = hdr.payload; + name_hash_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "OCSP: issuerNameHash", + name_hash, name_hash_len); + pos = hdr.payload + hdr.length; + + wpa_hexdump(MSG_DEBUG, "OCSP: Issuer subject DN", + issuer->subject_dn, issuer->subject_dn_len); + hash_len = ocsp_hash_data(&alg.oid, issuer->subject_dn, + issuer->subject_dn_len, hash); + if (hash_len == 0 || name_hash_len != hash_len || + os_memcmp(name_hash, hash, hash_len) != 0) { + wpa_printf(MSG_DEBUG, "OCSP: issuerNameHash mismatch"); + wpa_hexdump(MSG_DEBUG, "OCSP: Calculated issuerNameHash", + hash, hash_len); + return -1; + } + + /* issuerKeyHash OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected OCTET STRING (issuerKeyHash) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + key_hash = hdr.payload; + key_hash_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "OCSP: issuerKeyHash", key_hash, key_hash_len); + pos = hdr.payload + hdr.length; + + hash_len = ocsp_hash_data(&alg.oid, issuer->public_key, + issuer->public_key_len, hash); + if (hash_len == 0 || key_hash_len != hash_len || + os_memcmp(key_hash, hash, hash_len) != 0) { + wpa_printf(MSG_DEBUG, "OCSP: issuerKeyHash mismatch"); + wpa_hexdump(MSG_DEBUG, "OCSP: Calculated issuerKeyHash", + hash, hash_len); + return -1; + } + + /* serialNumber CertificateSerialNumber ::= INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER || + hdr.length < 1 || hdr.length > X509_MAX_SERIAL_NUM_LEN) { + wpa_printf(MSG_DEBUG, "OCSP: No INTEGER tag found for serialNumber; class=%d tag=0x%x length=%u", + hdr.class, hdr.tag, hdr.length); + return -1; + } + serial_number = hdr.payload; + serial_number_len = hdr.length; + while (serial_number_len > 0 && serial_number[0] == 0) { + serial_number++; + serial_number_len--; + } + wpa_hexdump(MSG_MSGDUMP, "OCSP: serialNumber", serial_number, + serial_number_len); + + if (serial_number_len != cert->serial_number_len || + os_memcmp(serial_number, cert->serial_number, + serial_number_len) != 0) { + wpa_printf(MSG_DEBUG, "OCSP: serialNumber mismatch"); + return -1; + } + + pos = end; + end = resp + len; + + /* certStatus CertStatus ::= CHOICE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected CHOICE (CertStatus) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + cert_status = hdr.tag; + wpa_printf(MSG_DEBUG, "OCSP: certStatus=%u", cert_status); + wpa_hexdump(MSG_DEBUG, "OCSP: CertStatus additional data", + hdr.payload, hdr.length); + pos = hdr.payload + hdr.length; + + os_get_time(&now); + /* thisUpdate GeneralizedTime */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_GENERALIZEDTIME || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, &update) < 0) { + wpa_printf(MSG_DEBUG, "OCSP: Failed to parse thisUpdate"); + return -1; + } + wpa_printf(MSG_DEBUG, "OCSP: thisUpdate %lu", (unsigned long) update); + pos = hdr.payload + hdr.length; + if ((unsigned long) now.sec < (unsigned long) update) { + wpa_printf(MSG_DEBUG, + "OCSP: thisUpdate time in the future (response not yet valid)"); + return -1; + } + + /* nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL */ + if (pos < end) { + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC && hdr.tag == 0) { + const u8 *next = hdr.payload + hdr.length; + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_GENERALIZEDTIME || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, + &update) < 0) { + wpa_printf(MSG_DEBUG, + "OCSP: Failed to parse nextUpdate"); + return -1; + } + wpa_printf(MSG_DEBUG, "OCSP: nextUpdate %lu", + (unsigned long) update); + pos = next; + if ((unsigned long) now.sec > (unsigned long) update) { + wpa_printf(MSG_DEBUG, "OCSP: nextUpdate time in the past (response has expired)"); + return -1; + } + } + } + + /* singleExtensions [1] EXPLICIT Extensions OPTIONAL */ + if (pos < end) { + wpa_hexdump(MSG_MSGDUMP, "OCSP: singleExtensions", + pos, end - pos); + /* Ignore for now */ + } + + if (cert_status == 0 /* good */) + *res = TLS_OCSP_GOOD; + else if (cert_status == 1 /* revoked */) + *res = TLS_OCSP_REVOKED; + else + return -1; + return 0; +} + + +static enum tls_ocsp_result +tls_process_ocsp_responses(struct tlsv1_client *conn, + struct x509_certificate *cert, + struct x509_certificate *issuer, const u8 *resp, + size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + enum tls_ocsp_result res; + + pos = resp; + end = resp + len; + while (pos < end) { + /* SingleResponse ::= SEQUENCE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (SingleResponse) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + if (tls_process_ocsp_single_response(conn, cert, issuer, + hdr.payload, hdr.length, + &res) == 0) + return res; + pos = hdr.payload + hdr.length; + } + + wpa_printf(MSG_DEBUG, + "OCSP: Did not find a response matching the server certificate"); + return TLS_OCSP_NO_RESPONSE; +} + + +static enum tls_ocsp_result +tls_process_basic_ocsp_response(struct tlsv1_client *conn, + struct x509_certificate *srv_cert, + const u8 *resp, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + const u8 *resp_data, *sign_value, *key_hash = NULL, *responses; + const u8 *resp_data_signed; + size_t resp_data_len, sign_value_len, responses_len; + size_t resp_data_signed_len; + struct x509_algorithm_identifier alg; + struct x509_certificate *certs = NULL, *last_cert = NULL; + struct x509_certificate *issuer, *signer; + struct x509_name name; /* used if key_hash == NULL */ + char buf[100]; + os_time_t produced_at; + enum tls_ocsp_result res; + + wpa_hexdump(MSG_MSGDUMP, "OCSP: BasicOCSPResponse", resp, len); + + os_memset(&name, 0, sizeof(name)); + + /* + * RFC 6960, 4.2.1: + * BasicOCSPResponse ::= SEQUENCE { + * tbsResponseData ResponseData, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + */ + + if (asn1_get_next(resp, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (BasicOCSPResponse) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* ResponseData ::= SEQUENCE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (ResponseData) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + resp_data = hdr.payload; + resp_data_len = hdr.length; + resp_data_signed = pos; + pos = hdr.payload + hdr.length; + resp_data_signed_len = pos - resp_data_signed; + + /* signatureAlgorithm AlgorithmIdentifier */ + if (x509_parse_algorithm_identifier(pos, end - pos, &alg, &pos)) + return TLS_OCSP_INVALID; + + /* signature BIT STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BITSTRING) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected BITSTRING (signature) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + if (hdr.length < 1) + return TLS_OCSP_INVALID; + pos = hdr.payload; + if (*pos) { + wpa_printf(MSG_DEBUG, "OCSP: BITSTRING - %d unused bits", *pos); + /* PKCS #1 v1.5 10.2.1: + * It is an error if the length in bits of the signature S is + * not a multiple of eight. + */ + return TLS_OCSP_INVALID; + } + sign_value = pos + 1; + sign_value_len = hdr.length - 1; + pos += hdr.length; + wpa_hexdump(MSG_MSGDUMP, "OCSP: signature", sign_value, sign_value_len); + + /* certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL */ + if (pos < end) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected [0] EXPLICIT (certs) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + wpa_hexdump(MSG_MSGDUMP, "OCSP: certs", + hdr.payload, hdr.length); + pos = hdr.payload; + end = hdr.payload + hdr.length; + while (pos < end) { + struct x509_certificate *cert; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (Certificate) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto fail; + } + + cert = x509_certificate_parse(hdr.payload, hdr.length); + if (!cert) + goto fail; + if (last_cert) { + last_cert->next = cert; + last_cert = cert; + } else { + last_cert = certs = cert; + } + pos = hdr.payload + hdr.length; + } + } + + /* + * ResponseData ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * responderID ResponderID, + * producedAt GeneralizedTime, + * responses SEQUENCE OF SingleResponse, + * responseExtensions [1] EXPLICIT Extensions OPTIONAL } + */ + pos = resp_data; + end = resp_data + resp_data_len; + wpa_hexdump(MSG_MSGDUMP, "OCSP: ResponseData", pos, end - pos); + + /* + * version [0] EXPLICIT Version DEFAULT v1 + * Version ::= INTEGER { v1(0) } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 && + hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC && + hdr.tag == 0) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER || + hdr.length != 1) { + wpa_printf(MSG_DEBUG, + "OCSP: No INTEGER (len=1) tag found for version field - found class %d tag 0x%x length %d", + hdr.class, hdr.tag, hdr.length); + goto fail; + } + wpa_printf(MSG_DEBUG, "OCSP: ResponseData version %u", + hdr.payload[0]); + if (hdr.payload[0] != 0) { + wpa_printf(MSG_DEBUG, + "OCSP: Unsupported ResponseData version %u", + hdr.payload[0]); + goto no_resp; + } + pos = hdr.payload + hdr.length; + } else { + wpa_printf(MSG_DEBUG, + "OCSP: Default ResponseData version (v1)"); + } + + /* + * ResponderID ::= CHOICE { + * byName [1] Name, + * byKey [2] KeyHash } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected CHOICE (ResponderID) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto fail; + } + + if (hdr.tag == 1) { + /* Name */ + if (x509_parse_name(hdr.payload, hdr.length, &name, &pos) < 0) + goto fail; + x509_name_string(&name, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "OCSP: ResponderID byName Name: %s", buf); + } else if (hdr.tag == 2) { + /* KeyHash ::= OCTET STRING */ + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected OCTET STRING (KeyHash) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto fail; + } + key_hash = hdr.payload; + wpa_hexdump(MSG_DEBUG, "OCSP: ResponderID byKey KeyHash", + key_hash, hdr.length); + if (hdr.length != SHA1_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "OCSP: Unexpected byKey KeyHash length %u - expected %u for SHA-1", + hdr.length, SHA1_MAC_LEN); + goto fail; + } + pos = hdr.payload + hdr.length; + } else { + wpa_printf(MSG_DEBUG, "OCSP: Unexpected ResponderID CHOICE %u", + hdr.tag); + goto fail; + } + + /* producedAt GeneralizedTime */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_GENERALIZEDTIME || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, + &produced_at) < 0) { + wpa_printf(MSG_DEBUG, "OCSP: Failed to parse producedAt"); + goto fail; + } + wpa_printf(MSG_DEBUG, "OCSP: producedAt %lu", + (unsigned long) produced_at); + pos = hdr.payload + hdr.length; + + /* responses SEQUENCE OF SingleResponse */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (responses) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto fail; + } + responses = hdr.payload; + responses_len = hdr.length; + wpa_hexdump(MSG_MSGDUMP, "OCSP: responses", responses, responses_len); + pos = hdr.payload + hdr.length; + + if (pos < end) { + /* responseExtensions [1] EXPLICIT Extensions OPTIONAL */ + wpa_hexdump(MSG_MSGDUMP, "OCSP: responseExtensions", + pos, end - pos); + /* Ignore for now. */ + } + + if (!srv_cert) { + wpa_printf(MSG_DEBUG, + "OCSP: Server certificate not known - cannot check OCSP response"); + goto no_resp; + } + + if (srv_cert->next) { + /* Issuer has already been verified in the chain */ + issuer = srv_cert->next; + } else { + /* Find issuer from the set of trusted certificates */ + for (issuer = conn->cred ? conn->cred->trusted_certs : NULL; + issuer; issuer = issuer->next) { + if (x509_name_compare(&srv_cert->issuer, + &issuer->subject) == 0) + break; + } + } + if (!issuer) { + wpa_printf(MSG_DEBUG, + "OCSP: Server certificate issuer not known - cannot check OCSP response"); + goto no_resp; + } + + if (ocsp_responder_id_match(issuer, &name, key_hash)) { + wpa_printf(MSG_DEBUG, + "OCSP: Server certificate issuer certificate matches ResponderID"); + signer = issuer; + } else { + for (signer = certs; signer; signer = signer->next) { + if (!ocsp_responder_id_match(signer, &name, key_hash) || + x509_name_compare(&srv_cert->issuer, + &issuer->subject) != 0 || + !(signer->ext_key_usage & + X509_EXT_KEY_USAGE_OCSP) || + x509_certificate_check_signature(issuer, signer) < + 0) + continue; + wpa_printf(MSG_DEBUG, + "OCSP: An extra certificate from the response matches ResponderID and is trusted as an OCSP signer"); + break; + } + if (!signer) { + wpa_printf(MSG_DEBUG, + "OCSP: Could not find OCSP signer certificate"); + goto no_resp; + } + } + + x509_free_name(&name); + os_memset(&name, 0, sizeof(name)); + x509_certificate_chain_free(certs); + certs = NULL; + + if (x509_check_signature(signer, &alg, sign_value, sign_value_len, + resp_data_signed, resp_data_signed_len) < 0) { + wpa_printf(MSG_DEBUG, "OCSP: Invalid signature"); + return TLS_OCSP_INVALID; + } + + res = tls_process_ocsp_responses(conn, srv_cert, issuer, + responses, responses_len); + if (res == TLS_OCSP_REVOKED) + srv_cert->ocsp_revoked = 1; + else if (res == TLS_OCSP_GOOD) + srv_cert->ocsp_good = 1; + return res; + +no_resp: + x509_free_name(&name); + x509_certificate_chain_free(certs); + return TLS_OCSP_NO_RESPONSE; + +fail: + x509_free_name(&name); + x509_certificate_chain_free(certs); + return TLS_OCSP_INVALID; +} + + +enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn, + const u8 *resp, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + u8 resp_status; + struct asn1_oid oid; + char obuf[80]; + struct x509_certificate *cert; + enum tls_ocsp_result res = TLS_OCSP_NO_RESPONSE; + enum tls_ocsp_result res_first = res; + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: OCSPResponse", resp, len); + + /* + * RFC 6960, 4.2.1: + * OCSPResponse ::= SEQUENCE { + * responseStatus OCSPResponseStatus, + * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + */ + + if (asn1_get_next(resp, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (OCSPResponse) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* OCSPResponseStatus ::= ENUMERATED */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_ENUMERATED || + hdr.length != 1) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected ENUMERATED (responseStatus) - found class %d tag 0x%x length %u", + hdr.class, hdr.tag, hdr.length); + return TLS_OCSP_INVALID; + } + resp_status = hdr.payload[0]; + wpa_printf(MSG_DEBUG, "OCSP: responseStatus %u", resp_status); + pos = hdr.payload + hdr.length; + if (resp_status != OCSP_RESP_STATUS_SUCCESSFUL) { + wpa_printf(MSG_DEBUG, "OCSP: No stapling result"); + return TLS_OCSP_NO_RESPONSE; + } + + /* responseBytes [0] EXPLICIT ResponseBytes OPTIONAL */ + if (pos == end) + return TLS_OCSP_NO_RESPONSE; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected [0] EXPLICIT (responseBytes) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + + /* + * ResponseBytes ::= SEQUENCE { + * responseType OBJECT IDENTIFIER, + * response OCTET STRING } + */ + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (ResponseBytes) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* responseType OBJECT IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "OCSP: Failed to parse OID (responseType)"); + return TLS_OCSP_INVALID; + } + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "OCSP: responseType %s", obuf); + if (!is_oid_basic_ocsp_resp(&oid)) { + wpa_printf(MSG_DEBUG, "OCSP: Ignore unsupported response type"); + return TLS_OCSP_NO_RESPONSE; + } + + /* response OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected OCTET STRING (response) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + + cert = conn->server_cert; + while (cert) { + if (!cert->ocsp_good && !cert->ocsp_revoked) { + char sbuf[128]; + + x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); + wpa_printf(MSG_DEBUG, + "OCSP: Trying to find certificate status for %s", + sbuf); + + res = tls_process_basic_ocsp_response(conn, cert, + hdr.payload, + hdr.length); + if (cert == conn->server_cert) + res_first = res; + } + if (res == TLS_OCSP_REVOKED || cert->issuer_trusted) + break; + cert = cert->next; + } + return res == TLS_OCSP_REVOKED ? res : res_first; +} diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c index 9ce96803753a9..244c3cb060821 100644 --- a/src/tls/tlsv1_client_read.c +++ b/src/tls/tlsv1_client_read.c @@ -1,6 +1,6 @@ /* * TLSv1 client - read handshake message - * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -27,6 +27,54 @@ static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len); +static int tls_version_disabled(struct tlsv1_client *conn, u16 ver) +{ + return (((conn->flags & TLS_CONN_DISABLE_TLSv1_0) && + ver == TLS_VERSION_1) || + ((conn->flags & TLS_CONN_DISABLE_TLSv1_1) && + ver == TLS_VERSION_1_1) || + ((conn->flags & TLS_CONN_DISABLE_TLSv1_2) && + ver == TLS_VERSION_1_2)); +} + + +static int tls_process_server_hello_extensions(struct tlsv1_client *conn, + const u8 *pos, size_t len) +{ + const u8 *end = pos + len; + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello extensions", + pos, len); + while (pos < end) { + u16 ext, elen; + + if (end - pos < 4) { + wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension header"); + return -1; + } + + ext = WPA_GET_BE16(pos); + pos += 2; + elen = WPA_GET_BE16(pos); + pos += 2; + + if (elen > end - pos) { + wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension"); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: ServerHello ExtensionType %u", + ext); + wpa_hexdump(MSG_DEBUG, "TLSv1: ServerHello extension data", + pos, elen); + + pos += elen; + } + + return 0; +} + + static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -76,7 +124,8 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, if (end - pos < 2) goto decode_error; tls_version = WPA_GET_BE16(pos); - if (!tls_version_ok(tls_version)) { + if (!tls_version_ok(tls_version) || + tls_version_disabled(conn, tls_version)) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " "ServerHello %u.%u", pos[0], pos[1]); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -165,8 +214,24 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, } pos++; + if (end - pos >= 2) { + u16 ext_len; + + ext_len = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < ext_len) { + wpa_printf(MSG_INFO, + "TLSv1: Invalid ServerHello extension length: %u (left: %u)", + ext_len, (unsigned int) (end - pos)); + goto decode_error; + } + + if (tls_process_server_hello_extensions(conn, pos, ext_len)) + goto decode_error; + pos += ext_len; + } + if (end != pos) { - /* TODO: ServerHello extensions */ wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the " "end of ServerHello", pos, end - pos); goto decode_error; @@ -211,6 +276,73 @@ decode_error: } +static void tls_peer_cert_event(struct tlsv1_client *conn, int depth, + struct x509_certificate *cert) +{ + union tls_event_data ev; + struct wpabuf *cert_buf = NULL; +#ifdef CONFIG_SHA256 + u8 hash[32]; +#endif /* CONFIG_SHA256 */ + char subject[128]; + + if (!conn->event_cb) + return; + + os_memset(&ev, 0, sizeof(ev)); + if (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; + } +#ifdef CONFIG_SHA256 + if (cert_buf) { + const u8 *addr[1]; + size_t len[1]; + addr[0] = wpabuf_head(cert_buf); + len[0] = wpabuf_len(cert_buf); + if (sha256_vector(1, addr, len, hash) == 0) { + ev.peer_cert.hash = hash; + ev.peer_cert.hash_len = sizeof(hash); + } + } +#endif /* CONFIG_SHA256 */ + + ev.peer_cert.depth = depth; + x509_name_string(&cert->subject, subject, sizeof(subject)); + ev.peer_cert.subject = subject; + + conn->event_cb(conn->cb_ctx, TLS_PEER_CERTIFICATE, &ev); + wpabuf_free(cert_buf); +} + + +static void tls_cert_chain_failure_event(struct tlsv1_client *conn, int depth, + struct x509_certificate *cert, + enum tls_fail_reason reason, + const char *reason_txt) +{ + struct wpabuf *cert_buf = NULL; + union tls_event_data ev; + char subject[128]; + + if (!conn->event_cb || !cert) + return; + + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.depth = depth; + x509_name_string(&cert->subject, subject, sizeof(subject)); + ev.peer_cert.subject = subject; + ev.cert_fail.reason = reason; + ev.cert_fail.reason_txt = reason_txt; + cert_buf = wpabuf_alloc_copy(cert->cert_start, + cert->cert_len); + ev.cert_fail.cert = cert_buf; + conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); + wpabuf_free(cert_buf); +} + + static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -354,6 +486,8 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, return -1; } + tls_peer_cert_event(conn, idx, cert); + if (last == NULL) chain = cert; else @@ -364,31 +498,99 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, pos += cert_len; } - if (conn->cred && - x509_certificate_chain_validate(conn->cred->trusted_certs, chain, - &reason, conn->disable_time_checks) - < 0) { + if (conn->cred && conn->cred->server_cert_only && chain) { + u8 hash[SHA256_MAC_LEN]; + char buf[128]; + + wpa_printf(MSG_DEBUG, + "TLSv1: Validate server certificate hash"); + x509_name_string(&chain->subject, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLSv1: 0: %s", buf); + if (sha256_vector(1, &chain->cert_start, &chain->cert_len, + hash) < 0 || + os_memcmp(conn->cred->srv_cert_hash, hash, + SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Server certificate hash mismatch"); + wpa_hexdump(MSG_MSGDUMP, "TLSv1: SHA256 hash", + hash, SHA256_MAC_LEN); + if (conn->event_cb) { + union tls_event_data ev; + + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.reason = TLS_FAIL_UNSPECIFIED; + ev.cert_fail.reason_txt = + "Server certificate mismatch"; + ev.cert_fail.subject = buf; + conn->event_cb(conn->cb_ctx, + TLS_CERT_CHAIN_FAILURE, &ev); + } + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + } else if (conn->cred && conn->cred->cert_probe) { + wpa_printf(MSG_DEBUG, + "TLSv1: Reject server certificate on probe-only rune"); + if (conn->event_cb) { + union tls_event_data ev; + char buf[128]; + + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.reason = TLS_FAIL_SERVER_CHAIN_PROBE; + ev.cert_fail.reason_txt = + "Server certificate chain probe"; + if (chain) { + x509_name_string(&chain->subject, buf, + sizeof(buf)); + ev.cert_fail.subject = buf; + } + conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE, + &ev); + } + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } else if (conn->cred && conn->cred->ca_cert_verify && + x509_certificate_chain_validate( + conn->cred->trusted_certs, chain, &reason, + !!(conn->flags & TLS_CONN_DISABLE_TIME_CHECKS)) + < 0) { int tls_reason; wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " "validation failed (reason=%d)", reason); switch (reason) { case X509_VALIDATE_BAD_CERTIFICATE: tls_reason = TLS_ALERT_BAD_CERTIFICATE; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE, + "bad certificate"); break; case X509_VALIDATE_UNSUPPORTED_CERTIFICATE: tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE; break; case X509_VALIDATE_CERTIFICATE_REVOKED: tls_reason = TLS_ALERT_CERTIFICATE_REVOKED; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_REVOKED, + "certificate revoked"); break; case X509_VALIDATE_CERTIFICATE_EXPIRED: tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_EXPIRED, + "certificate has expired or is not yet valid"); break; case X509_VALIDATE_CERTIFICATE_UNKNOWN: tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN; break; case X509_VALIDATE_UNKNOWN_CA: tls_reason = TLS_ALERT_UNKNOWN_CA; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_UNTRUSTED, + "unknown CA"); break; default: tls_reason = TLS_ALERT_BAD_CERTIFICATE; @@ -399,7 +601,25 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, return -1; } - x509_certificate_chain_free(chain); + if (conn->cred && !conn->cred->server_cert_only && chain && + (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) && + !(chain->ext_key_usage & + (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_SERVER_AUTH))) { + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE, + "certificate not allowed for server authentication"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + + if (conn->flags & TLS_CONN_REQUEST_OCSP) { + x509_certificate_chain_free(conn->server_cert); + conn->server_cert = chain; + } else { + x509_certificate_chain_free(chain); + } *in_len = end - in_data; @@ -507,7 +727,7 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, server_params_end = pos; if (key_exchange == TLS_KEY_X_DHE_RSA) { - u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + u8 hash[64]; int hlen; if (conn->rl.tls_version == TLS_VERSION_1_2) { @@ -524,18 +744,21 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, */ if (end - pos < 2) goto fail; - if (pos[0] != TLS_HASH_ALG_SHA256 || + if ((pos[0] != TLS_HASH_ALG_SHA256 && + pos[0] != TLS_HASH_ALG_SHA384 && + pos[0] != TLS_HASH_ALG_SHA512) || pos[1] != TLS_SIGN_ALG_RSA) { wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/signature(%u) algorithm", pos[0], pos[1]); goto fail; } - pos += 2; hlen = tlsv12_key_x_server_params_hash( - conn->rl.tls_version, conn->client_random, + conn->rl.tls_version, pos[0], + conn->client_random, conn->server_random, server_params, server_params_end - server_params, hash); + pos += 2; #else /* CONFIG_TLSV12 */ goto fail; #endif /* CONFIG_TLSV12 */ @@ -567,6 +790,229 @@ fail: } +static enum tls_ocsp_result +tls_process_certificate_status_ocsp_response(struct tlsv1_client *conn, + const u8 *pos, size_t len) +{ + const u8 *end = pos + len; + u32 ocsp_resp_len; + + /* opaque OCSPResponse<1..2^24-1>; */ + if (end - pos < 3) { + wpa_printf(MSG_INFO, "TLSv1: Too short OCSPResponse"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return TLS_OCSP_INVALID; + } + ocsp_resp_len = WPA_GET_BE24(pos); + pos += 3; + if (end - pos < ocsp_resp_len) { + wpa_printf(MSG_INFO, "TLSv1: Truncated OCSPResponse"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return TLS_OCSP_INVALID; + } + + return tls_process_ocsp_response(conn, pos, ocsp_resp_len); +} + + +static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type, status_type; + enum tls_ocsp_result res; + struct x509_certificate *cert; + int depth; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, + "TLSv1: Expected Handshake; received content type 0x%x", + ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, + "TLSv1: Too short CertificateStatus (left=%lu)", + (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, + "TLSv1: Mismatch in CertificateStatus length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS) { + wpa_printf(MSG_DEBUG, + "TLSv1: Received unexpected handshake message %d (expected CertificateStatus)", + type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateStatus"); + + /* + * struct { + * CertificateStatusType status_type; + * select (status_type) { + * case ocsp: OCSPResponse; + * case ocsp_multi: OCSPResponseList; + * } response; + * } CertificateStatus; + */ + if (end - pos < 1) { + wpa_printf(MSG_INFO, "TLSv1: Too short CertificateStatus"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + status_type = *pos++; + wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatus status_type %u", + status_type); + + if (status_type == 1 /* ocsp */) { + res = tls_process_certificate_status_ocsp_response( + conn, pos, end - pos); + } else if (status_type == 2 /* ocsp_multi */) { + int good = 0, revoked = 0; + u32 resp_len; + + res = TLS_OCSP_NO_RESPONSE; + + /* + * opaque OCSPResponse<0..2^24-1>; + * + * struct { + * OCSPResponse ocsp_response_list<1..2^24-1>; + * } OCSPResponseList; + */ + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, + "TLSv1: Truncated OCSPResponseList"); + res = TLS_OCSP_INVALID; + goto done; + } + resp_len = WPA_GET_BE24(pos); + pos += 3; + if (end - pos < resp_len) { + wpa_printf(MSG_DEBUG, + "TLSv1: Truncated OCSPResponseList(len=%u)", + resp_len); + res = TLS_OCSP_INVALID; + goto done; + } + end = pos + resp_len; + + while (end - pos >= 3) { + resp_len = WPA_GET_BE24(pos); + pos += 3; + if (resp_len > end - pos) { + wpa_printf(MSG_DEBUG, + "TLSv1: Truncated OCSPResponse(len=%u; left=%d) in ocsp_multi", + resp_len, (int) (end - pos)); + res = TLS_OCSP_INVALID; + break; + } + if (!resp_len) + continue; /* Skip an empty response */ + res = tls_process_certificate_status_ocsp_response( + conn, pos - 3, resp_len + 3); + if (res == TLS_OCSP_REVOKED) + revoked++; + else if (res == TLS_OCSP_GOOD) + good++; + pos += resp_len; + } + + if (revoked) + res = TLS_OCSP_REVOKED; + else if (good) + res = TLS_OCSP_GOOD; + } else { + wpa_printf(MSG_DEBUG, + "TLSv1: Ignore unsupported CertificateStatus"); + goto skip; + } + +done: + if (res == TLS_OCSP_REVOKED) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_CERTIFICATE_REVOKED); + for (cert = conn->server_cert, depth = 0; cert; + cert = cert->next, depth++) { + if (cert->ocsp_revoked) { + tls_cert_chain_failure_event( + conn, depth, cert, TLS_FAIL_REVOKED, + "certificate revoked"); + } + } + return -1; + } + + if (conn->flags & TLS_CONN_REQUIRE_OCSP_ALL) { + /* + * Verify that each certificate on the chain that is not part + * of the trusted certificates has a good status. If not, + * terminate handshake. + */ + for (cert = conn->server_cert, depth = 0; cert; + cert = cert->next, depth++) { + if (!cert->ocsp_good) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); + tls_cert_chain_failure_event( + conn, depth, cert, + TLS_FAIL_UNSPECIFIED, + "bad certificate status response"); + return -1; + } + if (cert->issuer_trusted) + break; + } + } + + if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && res != TLS_OCSP_GOOD) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + res == TLS_OCSP_INVALID ? TLS_ALERT_DECODE_ERROR : + TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); + if (conn->server_cert) + tls_cert_chain_failure_event( + conn, 0, conn->server_cert, + TLS_FAIL_UNSPECIFIED, + "bad certificate status response"); + return -1; + } + + conn->ocsp_resp_received = 1; + +skip: + *in_len = end - in_data; + + conn->state = SERVER_KEY_EXCHANGE; + + return 0; +} + + static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -608,6 +1054,10 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, end = pos + len; + if ((conn->flags & TLS_CONN_REQUEST_OCSP) && + type == TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS) + return tls_process_certificate_status(conn, ct, in_data, + in_len); if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) return tls_process_certificate_request(conn, ct, in_data, in_len); @@ -617,7 +1067,9 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected ServerKeyExchange/" - "CertificateRequest/ServerHelloDone)", type); + "CertificateRequest/ServerHelloDone%s)", type, + (conn->flags & TLS_CONN_REQUEST_OCSP) ? + "/CertificateStatus" : ""); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -771,6 +1223,15 @@ static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone"); + if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && + !conn->ocsp_resp_received) { + wpa_printf(MSG_INFO, + "TLSv1: No OCSP response received - reject handshake"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); + return -1; + } + *in_len = end - in_data; conn->state = CLIENT_KEY_EXCHANGE; diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c index d192f44f40882..04d895e61926a 100644 --- a/src/tls/tlsv1_client_write.c +++ b/src/tls/tlsv1_client_write.c @@ -1,6 +1,6 @@ /* * TLSv1 client - write handshake message - * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -47,8 +47,28 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr; struct os_time now; size_t len, i; + u8 *ext_start; + u16 tls_version = TLS_VERSION; - wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello"); + /* Pick the highest locally enabled TLS version */ +#ifdef CONFIG_TLSV12 + if ((conn->flags & TLS_CONN_DISABLE_TLSv1_2) && + tls_version == TLS_VERSION_1_2) + tls_version = TLS_VERSION_1_1; +#endif /* CONFIG_TLSV12 */ +#ifdef CONFIG_TLSV11 + if ((conn->flags & TLS_CONN_DISABLE_TLSv1_1) && + tls_version == TLS_VERSION_1_1) + tls_version = TLS_VERSION_1; +#endif /* CONFIG_TLSV11 */ + if ((conn->flags & TLS_CONN_DISABLE_TLSv1_0) && + tls_version == TLS_VERSION_1) { + wpa_printf(MSG_INFO, "TLSv1: No TLS version allowed"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello (ver %s)", + tls_version_str(tls_version)); *out_len = 0; os_get_time(&now); @@ -61,7 +81,7 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random", conn->client_random, TLS_RANDOM_LEN); - len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len; + len = 150 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len; hello = os_malloc(len); if (hello == NULL) return NULL; @@ -81,7 +101,7 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) pos += 3; /* body - ClientHello */ /* ProtocolVersion client_version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, tls_version); pos += 2; /* Random random: uint32 gmt_unix_time, opaque random_bytes */ os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN); @@ -101,12 +121,124 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) *pos++ = 1; *pos++ = TLS_COMPRESSION_NULL; + /* Extension */ + ext_start = pos; + pos += 2; + +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + /* + * Add signature_algorithms extension since we support only + * SHA256 (and not the default SHA1) with TLSv1.2. + */ + /* ExtensionsType extension_type = signature_algorithms(13) */ + WPA_PUT_BE16(pos, TLS_EXT_SIGNATURE_ALGORITHMS); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 8); + pos += 2; + /* supported_signature_algorithms<2..2^16-2> length */ + WPA_PUT_BE16(pos, 6); + pos += 2; + /* supported_signature_algorithms */ + *pos++ = TLS_HASH_ALG_SHA512; + *pos++ = TLS_SIGN_ALG_RSA; + *pos++ = TLS_HASH_ALG_SHA384; + *pos++ = TLS_SIGN_ALG_RSA; + *pos++ = TLS_HASH_ALG_SHA256; + *pos++ = TLS_SIGN_ALG_RSA; + } +#endif /* CONFIG_TLSV12 */ + if (conn->client_hello_ext) { os_memcpy(pos, conn->client_hello_ext, conn->client_hello_ext_len); pos += conn->client_hello_ext_len; } + if (conn->flags & TLS_CONN_REQUEST_OCSP) { + wpa_printf(MSG_DEBUG, + "TLSv1: Add status_request extension for OCSP stapling"); + /* ExtensionsType extension_type = status_request(5) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 5); + pos += 2; + + /* + * RFC 6066, 8: + * struct { + * CertificateStatusType status_type; + * select (status_type) { + * case ocsp: OCSPStatusRequest; + * } request; + * } CertificateStatusRequest; + * + * enum { ocsp(1), (255) } CertificateStatusType; + */ + *pos++ = 1; /* status_type = ocsp(1) */ + + /* + * struct { + * ResponderID responder_id_list<0..2^16-1>; + * Extensions request_extensions; + * } OCSPStatusRequest; + * + * opaque ResponderID<1..2^16-1>; + * opaque Extensions<0..2^16-1>; + */ + WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */ + pos += 2; + WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */ + pos += 2; + + wpa_printf(MSG_DEBUG, + "TLSv1: Add status_request_v2 extension for OCSP stapling"); + /* ExtensionsType extension_type = status_request_v2(17) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 7); + pos += 2; + + /* + * RFC 6961, 2.2: + * struct { + * CertificateStatusType status_type; + * uint16 request_length; + * select (status_type) { + * case ocsp: OCSPStatusRequest; + * case ocsp_multi: OCSPStatusRequest; + * } request; + * } CertificateStatusRequestItemV2; + * + * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType; + * + * struct { + * CertificateStatusRequestItemV2 + * certificate_status_req_list<1..2^16-1>; + * } CertificateStatusRequestListV2; + */ + + /* certificate_status_req_list<1..2^16-1> */ + WPA_PUT_BE16(pos, 5); + pos += 2; + + /* CertificateStatusRequestItemV2 */ + *pos++ = 2; /* status_type = ocsp_multi(2) */ + /* OCSPStatusRequest as shown above for v1 */ + WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */ + pos += 2; + WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */ + pos += 2; + } + + if (pos == ext_start + 2) + pos -= 2; /* no extensions */ + else + WPA_PUT_BE16(ext_start, pos - ext_start - 2); + WPA_PUT_BE24(hs_length, pos - hs_length - 3); tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); @@ -134,6 +266,11 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, struct x509_certificate *cert; pos = *msgpos; + if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); rhdr = pos; @@ -154,7 +291,7 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, pos += 3; cert = conn->cred ? conn->cred->cert : NULL; while (cert) { - if (pos + 3 + cert->cert_len > end) { + if (3 + cert->cert_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " "for Certificate (cert_len=%lu left=%lu)", (unsigned long) cert->cert_len, @@ -265,9 +402,16 @@ static int tlsv1_key_x_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)", dh_yc, dh_yc_len); + if (end - *pos < 2) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + os_free(dh_yc); + return -1; + } WPA_PUT_BE16(*pos, dh_yc_len); *pos += 2; - if (*pos + dh_yc_len > end) { + if (dh_yc_len > (size_t) (end - *pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the " "message buffer for Yc"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -789,6 +933,8 @@ static u8 * tls_send_change_cipher_spec(struct tlsv1_client *conn, wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed " "successfully"); + if (!conn->session_resumed && conn->use_session_ticket) + conn->session_resumed = 1; conn->state = ESTABLISHED; return msg; diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c index dabc12a129788..6b28417e499c7 100644 --- a/src/tls/tlsv1_common.c +++ b/src/tls/tlsv1_common.c @@ -335,7 +335,7 @@ int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, #ifdef CONFIG_TLSV12 -int tlsv12_key_x_server_params_hash(u16 tls_version, +int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_alg, const u8 *client_random, const u8 *server_random, const u8 *server_params, @@ -343,14 +343,30 @@ int tlsv12_key_x_server_params_hash(u16 tls_version, { size_t hlen; struct crypto_hash *ctx; - - ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0); + enum crypto_hash_alg alg; + + switch (hash_alg) { + case TLS_HASH_ALG_SHA256: + alg = CRYPTO_HASH_ALG_SHA256; + hlen = SHA256_MAC_LEN; + break; + case TLS_HASH_ALG_SHA384: + alg = CRYPTO_HASH_ALG_SHA384; + hlen = 48; + break; + case TLS_HASH_ALG_SHA512: + alg = CRYPTO_HASH_ALG_SHA512; + hlen = 64; + break; + default: + return -1; + } + ctx = crypto_hash_init(alg, NULL, 0); if (ctx == NULL) return -1; crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN); crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN); crypto_hash_update(ctx, server_params, server_params_len); - hlen = SHA256_MAC_LEN; if (crypto_hash_finish(ctx, hash, &hlen) < 0) return -1; @@ -469,6 +485,21 @@ int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk, wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-256"); decrypted = buf + 19; buflen -= 19; + } else if (buflen >= 19 + 48 && + os_memcmp(buf, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01" + "\x65\x03\x04\x02\x02\x05\x00\x04\x30", 19) == 0) + { + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-384"); + decrypted = buf + 19; + buflen -= 19; + } else if (buflen >= 19 + 64 && + os_memcmp(buf, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01" + "\x65\x03\x04\x02\x03\x05\x00\x04\x40", 19) == 0) + { + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-512"); + decrypted = buf + 19; + buflen -= 19; + } else { wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized DigestInfo"); os_free(buf); diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h index 26e68af166069..e30b15a030a80 100644 --- a/src/tls/tlsv1_common.h +++ b/src/tls/tlsv1_common.h @@ -169,6 +169,8 @@ enum { #define TLS_EXT_TRUSTED_CA_KEYS 3 /* RFC 4366 */ #define TLS_EXT_TRUNCATED_HMAC 4 /* RFC 4366 */ #define TLS_EXT_STATUS_REQUEST 5 /* RFC 4366 */ +#define TLS_EXT_SIGNATURE_ALGORITHMS 13 /* RFC 5246 */ +#define TLS_EXT_STATUS_REQUEST_V2 17 /* RFC 6961 */ #define TLS_EXT_SESSION_TICKET 35 /* RFC 4507 */ #define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */ @@ -257,7 +259,8 @@ int tls_version_ok(u16 ver); const char * tls_version_str(u16 ver); int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); -int tlsv12_key_x_server_params_hash(u16 tls_version, const u8 *client_random, +int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_Alg, + const u8 *client_random, const u8 *server_random, const u8 *server_params, size_t server_params_len, u8 *hash); diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c index 1ea6827b898ec..52c1ae0143da3 100644 --- a/src/tls/tlsv1_cred.c +++ b/src/tls/tlsv1_cred.c @@ -1,6 +1,6 @@ /* * TLSv1 credentials - * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,9 @@ #include "common.h" #include "base64.h" #include "crypto/crypto.h" +#include "crypto/sha1.h" +#include "pkcs5.h" +#include "pkcs8.h" #include "x509v3.h" #include "tlsv1_cred.h" @@ -33,6 +36,8 @@ void tlsv1_cred_free(struct tlsv1_credentials *cred) crypto_private_key_free(cred->key); os_free(cred->dh_p); os_free(cred->dh_g); + os_free(cred->ocsp_stapling_response); + os_free(cred->ocsp_stapling_response_multi); os_free(cred); } @@ -190,6 +195,43 @@ int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, const u8 *cert_blob, size_t cert_blob_len, const char *path) { + if (cert && os_strncmp(cert, "hash://", 7) == 0) { + const char *pos = cert + 7; + if (os_strncmp(pos, "server/sha256/", 14) != 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Unsupported ca_cert hash value '%s'", + cert); + return -1; + } + pos += 14; + if (os_strlen(pos) != 32 * 2) { + wpa_printf(MSG_DEBUG, + "TLSv1: Unexpected SHA256 hash length in ca_cert '%s'", + cert); + return -1; + } + if (hexstr2bin(pos, cred->srv_cert_hash, 32) < 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Invalid SHA256 hash value in ca_cert '%s'", + cert); + return -1; + } + cred->server_cert_only = 1; + cred->ca_cert_verify = 0; + wpa_printf(MSG_DEBUG, + "TLSv1: Checking only server certificate match"); + return 0; + } + + if (cert && os_strncmp(cert, "probe://", 8) == 0) { + cred->cert_probe = 1; + cred->ca_cert_verify = 0; + wpa_printf(MSG_DEBUG, "TLSv1: Only probe server certificate"); + return 0; + } + + cred->ca_cert_verify = cert || cert_blob || path; + if (tlsv1_set_cert_chain(&cred->trusted_certs, cert, cert_blob, cert_blob_len) < 0) return -1; @@ -288,6 +330,735 @@ static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key, } +#ifdef PKCS12_FUNCS + +static int oid_is_rsadsi(struct asn1_oid *oid) +{ + return oid->len >= 4 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */; +} + + +static int pkcs12_is_bagtype_oid(struct asn1_oid *oid, unsigned long type) +{ + return oid->len == 9 && + oid_is_rsadsi(oid) && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 12 /* pkcs-12 */ && + oid->oid[6] == 10 && + oid->oid[7] == 1 /* bagtypes */ && + oid->oid[8] == type; +} + + +static int is_oid_pkcs7(struct asn1_oid *oid) +{ + return oid->len == 7 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */ && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 7 /* pkcs-7 */; +} + + +static int is_oid_pkcs7_data(struct asn1_oid *oid) +{ + return is_oid_pkcs7(oid) && oid->oid[6] == 1 /* data */; +} + + +static int is_oid_pkcs7_enc_data(struct asn1_oid *oid) +{ + return is_oid_pkcs7(oid) && oid->oid[6] == 6 /* encryptedData */; +} + + +static int is_oid_pkcs9(struct asn1_oid *oid) +{ + return oid->len >= 6 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */ && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 9 /* pkcs-9 */; +} + + +static int is_oid_pkcs9_friendly_name(struct asn1_oid *oid) +{ + return oid->len == 7 && is_oid_pkcs9(oid) && + oid->oid[6] == 20; +} + + +static int is_oid_pkcs9_local_key_id(struct asn1_oid *oid) +{ + return oid->len == 7 && is_oid_pkcs9(oid) && + oid->oid[6] == 21; +} + + +static int is_oid_pkcs9_x509_cert(struct asn1_oid *oid) +{ + return oid->len == 8 && is_oid_pkcs9(oid) && + oid->oid[6] == 22 /* certTypes */ && + oid->oid[7] == 1 /* x509Certificate */; +} + + +static int pkcs12_keybag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len) +{ + /* TODO */ + return 0; +} + + +static int pkcs12_pkcs8_keybag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, + const char *passwd) +{ + struct crypto_private_key *key; + + /* PKCS8ShroudedKeyBag ::= EncryptedPrivateKeyInfo */ + key = pkcs8_enc_key_import(buf, len, passwd); + if (!key) + return -1; + + wpa_printf(MSG_DEBUG, + "PKCS #12: Successfully decrypted PKCS8ShroudedKeyBag"); + crypto_private_key_free(cred->key); + cred->key = key; + + return 0; +} + + +static int pkcs12_certbag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len) +{ + struct asn1_hdr hdr; + struct asn1_oid oid; + char obuf[80]; + const u8 *pos, *end; + + /* + * CertBag ::= SEQUENCE { + * certId BAG-TYPE.&id ({CertTypes}), + * certValue [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId}) + * } + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (CertBag) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Failed to parse OID (certId)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #12: certId %s", obuf); + + if (!is_oid_pkcs9_x509_cert(&oid)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Ignored unsupported certificate type (certId %s)", + obuf); + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (certValue) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (x509Certificate) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "PKCS #12: x509Certificate", + hdr.payload, hdr.length); + if (cred->cert) { + struct x509_certificate *cert; + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignore extra certificate"); + cert = x509_certificate_parse(hdr.payload, hdr.length); + if (!cert) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Failed to parse x509Certificate"); + return 0; + } + x509_certificate_chain_free(cert); + + return 0; + } + return tlsv1_set_cert(cred, NULL, hdr.payload, hdr.length); +} + + +static int pkcs12_parse_attr_friendly_name(const u8 *pos, const u8 *end) +{ + struct asn1_hdr hdr; + + /* + * RFC 2985, 5.5.1: + * friendlyName ATTRIBUTE ::= { + * WITH SYNTAX BMPString (SIZE(1..pkcs-9-ub-friendlyName)) + * EQUALITY MATCHING RULE caseIgnoreMatch + * SINGLE VALUE TRUE + * ID pkcs-9-at-friendlyName + * } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BMPSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected BMPSTRING (friendlyName) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + wpa_hexdump_ascii(MSG_DEBUG, "PKCS #12: friendlyName", + hdr.payload, hdr.length); + return 0; +} + + +static int pkcs12_parse_attr_local_key_id(const u8 *pos, const u8 *end) +{ + struct asn1_hdr hdr; + + /* + * RFC 2985, 5.5.2: + * localKeyId ATTRIBUTE ::= { + * WITH SYNTAX OCTET STRING + * EQUALITY MATCHING RULE octetStringMatch + * SINGLE VALUE TRUE + * ID pkcs-9-at-localKeyId + * } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (localKeyID) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "PKCS #12: localKeyID", + hdr.payload, hdr.length); + return 0; +} + + +static int pkcs12_parse_attr(const u8 *pos, size_t len) +{ + const u8 *end = pos + len; + struct asn1_hdr hdr; + struct asn1_oid a_oid; + char obuf[80]; + + /* + * PKCS12Attribute ::= SEQUENCE { + * attrId ATTRIBUTE.&id ({PKCS12AttrSet}), + * attrValues SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId}) + * } + */ + + if (asn1_get_oid(pos, end - pos, &a_oid, &pos)) { + wpa_printf(MSG_DEBUG, "PKCS #12: Failed to parse OID (attrId)"); + return -1; + } + + asn1_oid_to_str(&a_oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #12: attrId %s", obuf); + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SET) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SET (attrValues) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: attrValues", + hdr.payload, hdr.length); + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (is_oid_pkcs9_friendly_name(&a_oid)) + return pkcs12_parse_attr_friendly_name(pos, end); + if (is_oid_pkcs9_local_key_id(&a_oid)) + return pkcs12_parse_attr_local_key_id(pos, end); + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unknown attribute"); + return 0; +} + + +static int pkcs12_safebag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, const char *passwd) +{ + struct asn1_hdr hdr; + struct asn1_oid oid; + char obuf[80]; + const u8 *pos = buf, *end = buf + len; + const u8 *value; + size_t value_len; + + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: SafeBag", buf, len); + + /* BAG-TYPE ::= TYPE-IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Failed to parse OID (BAG-TYPE)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #12: BAG-TYPE %s", obuf); + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (bagValue) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + value = hdr.payload; + value_len = hdr.length; + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagValue", value, value_len); + pos = hdr.payload + hdr.length; + + if (pos < end) { + /* bagAttributes SET OF PKCS12Attribute OPTIONAL */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SET) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SET (bagAttributes) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagAttributes", + hdr.payload, hdr.length); + + pos = hdr.payload; + end = hdr.payload + hdr.length; + while (pos < end) { + /* PKCS12Attribute ::= SEQUENCE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (PKCS12Attribute) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (pkcs12_parse_attr(hdr.payload, hdr.length) < 0) + return -1; + pos = hdr.payload + hdr.length; + } + } + + if (pkcs12_is_bagtype_oid(&oid, 1)) + return pkcs12_keybag(cred, value, value_len); + if (pkcs12_is_bagtype_oid(&oid, 2)) + return pkcs12_pkcs8_keybag(cred, value, value_len, passwd); + if (pkcs12_is_bagtype_oid(&oid, 3)) + return pkcs12_certbag(cred, value, value_len); + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unsupported BAG-TYPE"); + return 0; +} + + +static int pkcs12_safecontents(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, + const char *passwd) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + + /* SafeContents ::= SEQUENCE OF SafeBag */ + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (SafeContents) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* + * SafeBag ::= SEQUENCE { + * bagId BAG-TYPE.&id ({PKCS12BagSet}) + * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}), + * bagAttributes SET OF PKCS12Attribute OPTIONAL + * } + */ + + while (pos < end) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (SafeBag) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (pkcs12_safebag(cred, hdr.payload, hdr.length, passwd) < 0) + return -1; + pos = hdr.payload + hdr.length; + } + + return 0; +} + + +static int pkcs12_parse_content_data(struct tlsv1_credentials *cred, + const u8 *pos, const u8 *end, + const char *passwd) +{ + struct asn1_hdr hdr; + + /* Data ::= OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (Data) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data", hdr.payload, hdr.length); + + return pkcs12_safecontents(cred, hdr.payload, hdr.length, passwd); +} + + +static int pkcs12_parse_content_enc_data(struct tlsv1_credentials *cred, + const u8 *pos, const u8 *end, + const char *passwd) +{ + struct asn1_hdr hdr; + struct asn1_oid oid; + char buf[80]; + const u8 *enc_alg; + u8 *data; + size_t enc_alg_len, data_len; + int res = -1; + + /* + * EncryptedData ::= SEQUENCE { + * version Version, + * encryptedContentInfo EncryptedContentInfo } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (EncryptedData) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + pos = hdr.payload; + + /* Version ::= INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, + "PKCS #12: No INTEGER tag found for version; class=%d tag=0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length != 1 || hdr.payload[0] != 0) { + wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized PKCS #7 version"); + return -1; + } + pos = hdr.payload + hdr.length; + + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: EncryptedContentInfo", + pos, end - pos); + + /* + * EncryptedContentInfo ::= SEQUENCE { + * contentType ContentType, + * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (EncryptedContentInfo) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + /* ContentType ::= OBJECT IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)"); + return -1; + } + asn1_oid_to_str(&oid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "PKCS #12: EncryptedContentInfo::contentType %s", + buf); + + if (!is_oid_pkcs7_data(&oid)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Unsupported EncryptedContentInfo::contentType %s", + buf); + return 0; + } + + /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "PKCS #12: Expected SEQUENCE (ContentEncryptionAlgorithmIdentifier) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + enc_alg = hdr.payload; + enc_alg_len = hdr.length; + pos = hdr.payload + hdr.length; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] IMPLICIT (encryptedContent) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + /* EncryptedContent ::= OCTET STRING */ + data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length, + passwd, &data_len); + if (data) { + wpa_hexdump_key(MSG_MSGDUMP, + "PKCS #12: Decrypted encryptedContent", + data, data_len); + res = pkcs12_safecontents(cred, data, data_len, passwd); + os_free(data); + } + + return res; +} + + +static int pkcs12_parse_content(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, + const char *passwd) +{ + const u8 *pos = buf; + const u8 *end = buf + len; + struct asn1_oid oid; + char txt[80]; + struct asn1_hdr hdr; + + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: ContentInfo", buf, len); + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)"); + return 0; + } + + asn1_oid_to_str(&oid, txt, sizeof(txt)); + wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", txt); + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (content) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + pos = hdr.payload; + + if (is_oid_pkcs7_data(&oid)) + return pkcs12_parse_content_data(cred, pos, end, passwd); + if (is_oid_pkcs7_enc_data(&oid)) + return pkcs12_parse_content_enc_data(cred, pos, end, passwd); + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignored unsupported contentType %s", + txt); + + return 0; +} + + +static int pkcs12_parse(struct tlsv1_credentials *cred, + const u8 *key, size_t len, const char *passwd) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + struct asn1_oid oid; + char buf[80]; + + /* + * PFX ::= SEQUENCE { + * version INTEGER {v3(3)}(v3,...), + * authSafe ContentInfo, + * macData MacData OPTIONAL + * } + */ + + if (asn1_get_next(key, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (PFX) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, + "PKCS #12: No INTEGER tag found for version; class=%d tag=0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length != 1 || hdr.payload[0] != 3) { + wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized version"); + return -1; + } + pos = hdr.payload + hdr.length; + + /* + * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } + */ + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (authSafe) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + /* ContentType ::= OBJECT IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Could not find OBJECT IDENTIFIER (contentType); assume PKCS #12 not used"); + return -1; + } + asn1_oid_to_str(&oid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", buf); + if (!is_oid_pkcs7_data(&oid)) { + wpa_printf(MSG_DEBUG, "PKCS #12: Unsupported contentType %s", + buf); + return -1; + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (content) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + + /* Data ::= OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (Data) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + /* + * AuthenticatedSafe ::= SEQUENCE OF ContentInfo + * -- Data if unencrypted + * -- EncryptedData if password-encrypted + * -- EnvelopedData if public key-encrypted + */ + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data content", + hdr.payload, hdr.length); + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE within Data content - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + while (end > pos) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (ContentInfo) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + if (pkcs12_parse_content(cred, hdr.payload, hdr.length, + passwd) < 0) + return -1; + + pos = hdr.payload + hdr.length; + } + + return 0; +} + +#endif /* PKCS12_FUNCS */ + + static int tlsv1_set_key(struct tlsv1_credentials *cred, const u8 *key, size_t len, const char *passwd) { @@ -296,6 +1067,10 @@ static int tlsv1_set_key(struct tlsv1_credentials *cred, cred->key = tlsv1_set_key_pem(key, len); if (cred->key == NULL) cred->key = tlsv1_set_key_enc_pem(key, len, passwd); +#ifdef PKCS12_FUNCS + if (!cred->key) + pkcs12_parse(cred, key, len, passwd); +#endif /* PKCS12_FUNCS */ if (cred->key == NULL) { wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key"); return -1; diff --git a/src/tls/tlsv1_cred.h b/src/tls/tlsv1_cred.h index 68fbdc9230084..716e93c39006c 100644 --- a/src/tls/tlsv1_cred.h +++ b/src/tls/tlsv1_cred.h @@ -14,11 +14,19 @@ struct tlsv1_credentials { struct x509_certificate *cert; struct crypto_private_key *key; + unsigned int cert_probe:1; + unsigned int ca_cert_verify:1; + unsigned int server_cert_only:1; + u8 srv_cert_hash[32]; + /* Diffie-Hellman parameters */ u8 *dh_p; /* prime */ size_t dh_p_len; u8 *dh_g; /* generator */ size_t dh_g_len; + + char *ocsp_stapling_response; + char *ocsp_stapling_response_multi; }; diff --git a/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h index 96d79b3a8ba25..29c6678772150 100644 --- a/src/tls/tlsv1_server_i.h +++ b/src/tls/tlsv1_server_i.h @@ -55,6 +55,9 @@ struct tlsv1_server { void *log_cb_ctx; int use_session_ticket; + unsigned int status_request:1; + unsigned int status_request_v2:1; + unsigned int status_request_multi:1; u8 *dh_secret; size_t dh_secret_len; diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c index 0f237baff9db6..4aa8a019f3e6e 100644 --- a/src/tls/tlsv1_server_read.c +++ b/src/tls/tlsv1_server_read.c @@ -46,6 +46,78 @@ static int testing_cipher_suite_filter(struct tlsv1_server *conn, u16 suite) } +static void tls_process_status_request_item(struct tlsv1_server *conn, + const u8 *req, size_t req_len) +{ + const u8 *pos, *end; + u8 status_type; + + pos = req; + end = req + req_len; + + /* + * RFC 6961, 2.2: + * struct { + * CertificateStatusType status_type; + * uint16 request_length; + * select (status_type) { + * case ocsp: OCSPStatusRequest; + * case ocsp_multi: OCSPStatusRequest; + * } request; + * } CertificateStatusRequestItemV2; + * + * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType; + */ + + if (end - pos < 1) + return; /* Truncated data */ + + status_type = *pos++; + wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatusType %u", status_type); + if (status_type != 1 && status_type != 2) + return; /* Unsupported status type */ + /* + * For now, only OCSP stapling is supported, so ignore the specific + * request, if any. + */ + wpa_hexdump(MSG_DEBUG, "TLSv1: OCSPStatusRequest", pos, end - pos); + + if (status_type == 2) + conn->status_request_multi = 1; +} + + +static void tls_process_status_request_v2(struct tlsv1_server *conn, + const u8 *ext, size_t ext_len) +{ + const u8 *pos, *end; + + conn->status_request_v2 = 1; + + pos = ext; + end = ext + ext_len; + + /* + * RFC 6961, 2.2: + * struct { + * CertificateStatusRequestItemV2 + * certificate_status_req_list<1..2^16-1>; + * } CertificateStatusRequestListV2; + */ + + while (end - pos >= 2) { + u16 len; + + len = WPA_GET_BE16(pos); + pos += 2; + if (len > end - pos) + break; /* Truncated data */ + tls_process_status_request_item(conn, pos, len); + pos += len; + } +} + + static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -267,6 +339,11 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, ext_len); conn->session_ticket_len = ext_len; } + } else if (ext_type == TLS_EXT_STATUS_REQUEST) { + conn->status_request = 1; + } else if (ext_type == TLS_EXT_STATUS_REQUEST_V2) { + tls_process_status_request_v2(conn, pos, + ext_len); } pos += ext_len; @@ -471,6 +548,15 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, return -1; } + if (chain && (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) && + !(chain->ext_key_usage & + (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_CLIENT_AUTH))) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + x509_certificate_chain_free(chain); *in_len = end - in_data; diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c index 15e6692178ff8..bdc6c11992384 100644 --- a/src/tls/tlsv1_server_write.c +++ b/src/tls/tlsv1_server_write.c @@ -42,7 +42,7 @@ static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn) static int tls_write_server_hello(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos, *rhdr, *hs_start, *hs_length, *ext_start; struct os_time now; size_t rlen; @@ -97,6 +97,32 @@ static int tls_write_server_hello(struct tlsv1_server *conn, /* CompressionMethod compression_method */ *pos++ = TLS_COMPRESSION_NULL; + /* Extension */ + ext_start = pos; + pos += 2; + + if (conn->status_request) { + /* Add a status_request extension with empty extension_data */ + /* ExtensionsType extension_type = status_request(5) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 0); + pos += 2; + } + + if (conn->status_request_v2) { + /* + Add a status_request_v2 extension with empty extension_data + */ + /* ExtensionsType extension_type = status_request_v2(17) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 0); + pos += 2; + } + if (conn->session_ticket && conn->session_ticket_cb) { int res = conn->session_ticket_cb( conn->session_ticket_cb_ctx, @@ -133,6 +159,11 @@ static int tls_write_server_hello(struct tlsv1_server *conn, */ } + if (pos == ext_start + 2) + pos -= 2; /* no extensions */ + else + WPA_PUT_BE16(ext_start, pos - ext_start - 2); + WPA_PUT_BE24(hs_length, pos - hs_length - 3); tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); @@ -168,6 +199,11 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, } pos = *msgpos; + if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } tlsv1_server_log(conn, "Send Certificate"); rhdr = pos; @@ -188,7 +224,7 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, pos += 3; cert = conn->cred->cert; while (cert) { - if (pos + 3 + cert->cert_len > end) { + if (3 + cert->cert_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " "for Certificate (cert_len=%lu left=%lu)", (unsigned long) cert->cert_len, @@ -239,6 +275,93 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, } +static int tls_write_server_certificate_status(struct tlsv1_server *conn, + u8 **msgpos, u8 *end, + int ocsp_multi, + char *ocsp_resp, + size_t ocsp_resp_len) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen; + + if (!ocsp_resp) { + /* + * Client did not request certificate status or there is no + * matching response cached. + */ + return 0; + } + + pos = *msgpos; + if (TLS_RECORD_HEADER_LEN + 1 + 3 + 1 + 3 + ocsp_resp_len > + (unsigned int) (end - pos)) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + tlsv1_server_log(conn, "Send CertificateStatus (multi=%d)", ocsp_multi); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + + /* body - CertificateStatus + * + * struct { + * CertificateStatusType status_type; + * select (status_type) { + * case ocsp: OCSPResponse; + * case ocsp_multi: OCSPResponseList; + * } response; + * } CertificateStatus; + * + * opaque OCSPResponse<1..2^24-1>; + * + * struct { + * OCSPResponse ocsp_response_list<1..2^24-1>; + * } OCSPResponseList; + */ + + /* CertificateStatusType status_type */ + if (ocsp_multi) + *pos++ = 2; /* ocsp_multi(2) */ + else + *pos++ = 1; /* ocsp(1) */ + /* uint24 length of OCSPResponse */ + WPA_PUT_BE24(pos, ocsp_resp_len); + pos += 3; + os_memcpy(pos, ocsp_resp, ocsp_resp_len); + pos += ocsp_resp_len; + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + static int tls_write_server_key_exchange(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { @@ -371,7 +494,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, /* body - ServerDHParams */ server_params = pos; /* dh_p */ - if (pos + 2 + dh_p_len > end) { + if (2 + dh_p_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_p"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -385,7 +508,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos += dh_p_len; /* dh_g */ - if (pos + 2 + conn->cred->dh_g_len > end) { + if (2 + conn->cred->dh_g_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_g"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -399,7 +522,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos += conn->cred->dh_g_len; /* dh_Ys */ - if (pos + 2 + dh_ys_len > end) { + if (2 + dh_ys_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_Ys"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -443,7 +566,8 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, if (conn->rl.tls_version >= TLS_VERSION_1_2) { #ifdef CONFIG_TLSV12 hlen = tlsv12_key_x_server_params_hash( - conn->rl.tls_version, conn->client_random, + conn->rl.tls_version, TLS_HASH_ALG_SHA256, + conn->client_random, conn->server_random, server_params, pos - server_params, hash + 19); @@ -457,7 +581,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, * SignatureAlgorithm signature; * } SignatureAndHashAlgorithm; */ - if (hlen < 0 || pos + 2 > end) { + if (hlen < 0 || end - pos < 2) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; @@ -804,24 +928,46 @@ static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len) { u8 *msg, *end, *pos; size_t msglen; + int ocsp_multi = 0; + char *ocsp_resp = NULL; + size_t ocsp_resp_len = 0; *out_len = 0; - msglen = 1000 + tls_server_cert_chain_der_len(conn); + if (conn->status_request_multi && + conn->cred->ocsp_stapling_response_multi) { + ocsp_resp = os_readfile( + conn->cred->ocsp_stapling_response_multi, + &ocsp_resp_len); + ocsp_multi = 1; + } else if ((conn->status_request || conn->status_request_v2) && + conn->cred->ocsp_stapling_response) { + ocsp_resp = os_readfile(conn->cred->ocsp_stapling_response, + &ocsp_resp_len); + } + if (!ocsp_resp) + ocsp_resp_len = 0; + + msglen = 1000 + tls_server_cert_chain_der_len(conn) + ocsp_resp_len; msg = os_malloc(msglen); - if (msg == NULL) + if (msg == NULL) { + os_free(ocsp_resp); return NULL; + } pos = msg; end = msg + msglen; if (tls_write_server_hello(conn, &pos, end) < 0) { os_free(msg); + os_free(ocsp_resp); return NULL; } if (conn->use_session_ticket) { + os_free(ocsp_resp); + /* Abbreviated handshake using session ticket; RFC 4507 */ if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 || tls_write_server_finished(conn, &pos, end) < 0) { @@ -838,12 +984,16 @@ static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len) /* Full handshake */ if (tls_write_server_certificate(conn, &pos, end) < 0 || + tls_write_server_certificate_status(conn, &pos, end, ocsp_multi, + ocsp_resp, ocsp_resp_len) < 0 || tls_write_server_key_exchange(conn, &pos, end) < 0 || tls_write_server_certificate_request(conn, &pos, end) < 0 || tls_write_server_hello_done(conn, &pos, end) < 0) { os_free(msg); + os_free(ocsp_resp); return NULL; } + os_free(ocsp_resp); *out_len = pos - msg; diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c index b51dfcd447320..75f222c4f2495 100644 --- a/src/tls/x509v3.c +++ b/src/tls/x509v3.c @@ -1,6 +1,6 @@ /* * X.509v3 certificate parsing and processing (RFC 3280 profile) - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,7 +14,7 @@ #include "x509v3.h" -static void x509_free_name(struct x509_name *name) +void x509_free_name(struct x509_name *name) { size_t i; @@ -55,6 +55,7 @@ void x509_certificate_free(struct x509_certificate *cert) x509_free_name(&cert->subject); os_free(cert->public_key); os_free(cert->sign_value); + os_free(cert->subject_dn); os_free(cert); } @@ -177,9 +178,9 @@ int x509_name_compare(struct x509_name *a, struct x509_name *b) } -static int x509_parse_algorithm_identifier( - const u8 *buf, size_t len, - struct x509_algorithm_identifier *id, const u8 **next) +int x509_parse_algorithm_identifier(const u8 *buf, size_t len, + struct x509_algorithm_identifier *id, + const u8 **next) { struct asn1_hdr hdr; const u8 *pos, *end; @@ -199,12 +200,11 @@ static int x509_parse_algorithm_identifier( hdr.class, hdr.tag); return -1; } + if (hdr.length > buf + len - hdr.payload) + return -1; pos = hdr.payload; end = pos + hdr.length; - if (end > buf + len) - return -1; - *next = end; if (asn1_get_oid(pos, end - pos, &id->oid, &pos)) @@ -243,7 +243,7 @@ static int x509_parse_public_key(const u8 *buf, size_t len, } pos = hdr.payload; - if (pos + hdr.length > end) + if (hdr.length > end - pos) return -1; end = pos + hdr.length; *next = end; @@ -289,8 +289,8 @@ static int x509_parse_public_key(const u8 *buf, size_t len, } -static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, - const u8 **next) +int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, + const u8 **next) { struct asn1_hdr hdr; const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end; @@ -319,7 +319,7 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, } pos = hdr.payload; - if (pos + hdr.length > buf + len) + if (hdr.length > buf + len - pos) return -1; end = *next = pos + hdr.length; @@ -537,8 +537,7 @@ done: } -static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, - os_time_t *val) +int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val) { const char *pos; int year, month, day, hour, min, sec; @@ -677,7 +676,7 @@ static int x509_parse_validity(const u8 *buf, size_t len, pos = hdr.payload; plen = hdr.length; - if (pos + plen > buf + len) + if (plen > (size_t) (buf + len - pos)) return -1; *next = pos + plen; @@ -721,6 +720,15 @@ static int x509_id_ce_oid(struct asn1_oid *oid) } +static int x509_any_ext_key_usage_oid(struct asn1_oid *oid) +{ + return oid->len == 6 && + x509_id_ce_oid(oid) && + oid->oid[3] == 37 /* extKeyUsage */ && + oid->oid[4] == 0 /* anyExtendedKeyUsage */; +} + + static int x509_parse_ext_key_usage(struct x509_certificate *cert, const u8 *pos, size_t len) { @@ -801,7 +809,7 @@ static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, } cert->ca = hdr.payload[0]; - if (hdr.payload + hdr.length == pos + len) { + if (hdr.length == pos + len - hdr.payload) { wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d", cert->ca); return 0; @@ -1074,6 +1082,112 @@ static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert, } +static int x509_id_pkix_oid(struct asn1_oid *oid) +{ + return oid->len >= 7 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 3 /* identified-organization */ && + oid->oid[2] == 6 /* dod */ && + oid->oid[3] == 1 /* internet */ && + oid->oid[4] == 5 /* security */ && + oid->oid[5] == 5 /* mechanisms */ && + oid->oid[6] == 7 /* id-pkix */; +} + + +static int x509_id_kp_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len >= 8 && + x509_id_pkix_oid(oid) && + oid->oid[7] == 3 /* id-kp */; +} + + +static int x509_id_kp_server_auth_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len == 9 && + x509_id_kp_oid(oid) && + oid->oid[8] == 1 /* id-kp-serverAuth */; +} + + +static int x509_id_kp_client_auth_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len == 9 && + x509_id_kp_oid(oid) && + oid->oid[8] == 2 /* id-kp-clientAuth */; +} + + +static int x509_id_kp_ocsp_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len == 9 && + x509_id_kp_oid(oid) && + oid->oid[8] == 9 /* id-kp-OCSPSigning */; +} + + +static int x509_parse_ext_ext_key_usage(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + const u8 *end; + struct asn1_oid oid; + + /* + * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + * + * KeyPurposeId ::= OBJECT IDENTIFIER + */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(ExtKeyUsageSyntax) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length > pos + len - hdr.payload) + return -1; + pos = hdr.payload; + end = pos + hdr.length; + + wpa_hexdump(MSG_MSGDUMP, "X509: ExtKeyUsageSyntax", pos, end - pos); + + while (pos < end) { + char buf[80]; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) + return -1; + if (x509_any_ext_key_usage_oid(&oid)) { + os_strlcpy(buf, "anyExtendedKeyUsage", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_ANY; + } else if (x509_id_kp_server_auth_oid(&oid)) { + os_strlcpy(buf, "id-kp-serverAuth", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_SERVER_AUTH; + } else if (x509_id_kp_client_auth_oid(&oid)) { + os_strlcpy(buf, "id-kp-clientAuth", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_CLIENT_AUTH; + } else if (x509_id_kp_ocsp_oid(&oid)) { + os_strlcpy(buf, "id-kp-OCSPSigning", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_OCSP; + } else { + asn1_oid_to_str(&oid, buf, sizeof(buf)); + } + wpa_printf(MSG_DEBUG, "ExtKeyUsage KeyPurposeId: %s", buf); + } + + cert->extensions_present |= X509_EXT_EXT_KEY_USAGE; + + return 0; +} + + static int x509_parse_extension_data(struct x509_certificate *cert, struct asn1_oid *oid, const u8 *pos, size_t len) @@ -1085,7 +1199,6 @@ static int x509_parse_extension_data(struct x509_certificate *cert, * certificate policies (section 4.2.1.5) * name constraints (section 4.2.1.11) * policy constraints (section 4.2.1.12) - * extended key usage (section 4.2.1.13) * inhibit any-policy (section 4.2.1.15) */ switch (oid->oid[3]) { @@ -1097,6 +1210,8 @@ static int x509_parse_extension_data(struct x509_certificate *cert, return x509_parse_ext_issuer_alt_name(cert, pos, len); case 19: /* id-ce-basicConstraints */ return x509_parse_ext_basic_constraints(cert, pos, len); + case 37: /* id-ce-extKeyUsage */ + return x509_parse_ext_ext_key_usage(cert, pos, len); default: return 1; } @@ -1224,6 +1339,7 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, size_t left; char sbuf[128]; unsigned long value; + const u8 *subject_dn; /* tbsCertificate TBSCertificate ::= SEQUENCE */ if (asn1_get_next(buf, len, &hdr) < 0 || @@ -1287,21 +1403,23 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, /* serialNumber CertificateSerialNumber ::= INTEGER */ if (hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_INTEGER) { + hdr.tag != ASN1_TAG_INTEGER || + hdr.length < 1 || hdr.length > X509_MAX_SERIAL_NUM_LEN) { wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " - "serialNumber; class=%d tag=0x%x", - hdr.class, hdr.tag); + "serialNumber; class=%d tag=0x%x length=%u", + hdr.class, hdr.tag, hdr.length); return -1; } - pos = hdr.payload; - left = hdr.length; - while (left) { - cert->serial_number <<= 8; - cert->serial_number |= *pos++; - left--; + pos = hdr.payload + hdr.length; + while (hdr.length > 0 && hdr.payload[0] == 0) { + hdr.payload++; + hdr.length--; } - wpa_printf(MSG_MSGDUMP, "X509: serialNumber %lu", cert->serial_number); + os_memcpy(cert->serial_number, hdr.payload, hdr.length); + cert->serial_number_len = hdr.length; + wpa_hexdump(MSG_MSGDUMP, "X509: serialNumber", cert->serial_number, + cert->serial_number_len); /* signature AlgorithmIdentifier */ if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature, @@ -1319,8 +1437,14 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, return -1; /* subject Name */ + subject_dn = pos; if (x509_parse_name(pos, end - pos, &cert->subject, &pos)) return -1; + cert->subject_dn = os_malloc(pos - subject_dn); + if (!cert->subject_dn) + return -1; + cert->subject_dn_len = pos - subject_dn; + os_memcpy(cert->subject_dn, subject_dn, cert->subject_dn_len); x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf); @@ -1437,7 +1561,7 @@ static int x509_digest_oid(struct asn1_oid *oid) } -static int x509_sha1_oid(struct asn1_oid *oid) +int x509_sha1_oid(struct asn1_oid *oid) { return oid->len == 6 && oid->oid[0] == 1 /* iso */ && @@ -1449,7 +1573,7 @@ static int x509_sha1_oid(struct asn1_oid *oid) } -static int x509_sha256_oid(struct asn1_oid *oid) +static int x509_sha2_oid(struct asn1_oid *oid) { return oid->len == 9 && oid->oid[0] == 2 /* joint-iso-itu-t */ && @@ -1459,11 +1583,31 @@ static int x509_sha256_oid(struct asn1_oid *oid) oid->oid[4] == 101 /* gov */ && oid->oid[5] == 3 /* csor */ && oid->oid[6] == 4 /* nistAlgorithm */ && - oid->oid[7] == 2 /* hashAlgs */ && + oid->oid[7] == 2 /* hashAlgs */; +} + + +int x509_sha256_oid(struct asn1_oid *oid) +{ + return x509_sha2_oid(oid) && oid->oid[8] == 1 /* sha256 */; } +int x509_sha384_oid(struct asn1_oid *oid) +{ + return x509_sha2_oid(oid) && + oid->oid[8] == 2 /* sha384 */; +} + + +int x509_sha512_oid(struct asn1_oid *oid) +{ + return x509_sha2_oid(oid) && + oid->oid[8] == 3 /* sha512 */; +} + + /** * x509_certificate_parse - Parse a X.509 certificate in DER format * @buf: Pointer to the X.509 certificate in DER format @@ -1503,12 +1647,12 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) } pos = hdr.payload; - if (pos + hdr.length > end) { + if (hdr.length > end - pos) { x509_certificate_free(cert); return NULL; } - if (pos + hdr.length < end) { + if (hdr.length < end - pos) { wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER " "encoded certificate", pos + hdr.length, end - (pos + hdr.length)); @@ -1582,18 +1726,31 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) int x509_certificate_check_signature(struct x509_certificate *issuer, struct x509_certificate *cert) { + return x509_check_signature(issuer, &cert->signature, + cert->sign_value, cert->sign_value_len, + cert->tbs_cert_start, cert->tbs_cert_len); +} + + +int x509_check_signature(struct x509_certificate *issuer, + struct x509_algorithm_identifier *signature, + const u8 *sign_value, size_t sign_value_len, + const u8 *signed_data, size_t signed_data_len) +{ struct crypto_public_key *pk; u8 *data; const u8 *pos, *end, *next, *da_end; size_t data_len; struct asn1_hdr hdr; struct asn1_oid oid; - u8 hash[32]; + u8 hash[64]; size_t hash_len; + const u8 *addr[1] = { signed_data }; + size_t len[1] = { signed_data_len }; - if (!x509_pkcs_oid(&cert->signature.oid) || - cert->signature.oid.len != 7 || - cert->signature.oid.oid[5] != 1 /* pkcs-1 */) { + if (!x509_pkcs_oid(&signature->oid) || + signature->oid.len != 7 || + signature->oid.oid[5] != 1 /* pkcs-1 */) { wpa_printf(MSG_DEBUG, "X509: Unrecognized signature " "algorithm"); return -1; @@ -1604,15 +1761,15 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, if (pk == NULL) return -1; - data_len = cert->sign_value_len; + data_len = sign_value_len; data = os_malloc(data_len); if (data == NULL) { crypto_public_key_free(pk); return -1; } - if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value, - cert->sign_value_len, data, + if (crypto_public_key_decrypt_pkcs1(pk, sign_value, + sign_value_len, data, &data_len) < 0) { wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature"); crypto_public_key_free(pk); @@ -1675,12 +1832,11 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, } if (x509_sha1_oid(&oid)) { - if (cert->signature.oid.oid[6] != - 5 /* sha-1WithRSAEncryption */) { + if (signature->oid.oid[6] != 5 /* sha-1WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 " "does not match with certificate " "signatureAlgorithm (%lu)", - cert->signature.oid.oid[6]); + signature->oid.oid[6]); os_free(data); return -1; } @@ -1688,12 +1844,36 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, } if (x509_sha256_oid(&oid)) { - if (cert->signature.oid.oid[6] != + if (signature->oid.oid[6] != 11 /* sha2561WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 " "does not match with certificate " "signatureAlgorithm (%lu)", - cert->signature.oid.oid[6]); + signature->oid.oid[6]); + os_free(data); + return -1; + } + goto skip_digest_oid; + } + + if (x509_sha384_oid(&oid)) { + if (signature->oid.oid[6] != 12 /* sha384WithRSAEncryption */) { + wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA384 " + "does not match with certificate " + "signatureAlgorithm (%lu)", + signature->oid.oid[6]); + os_free(data); + return -1; + } + goto skip_digest_oid; + } + + if (x509_sha512_oid(&oid)) { + if (signature->oid.oid[6] != 13 /* sha512WithRSAEncryption */) { + wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA512 " + "does not match with certificate " + "signatureAlgorithm (%lu)", + signature->oid.oid[6]); os_free(data); return -1; } @@ -1707,12 +1887,11 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, } switch (oid.oid[5]) { case 5: /* md5 */ - if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */) - { + if (signature->oid.oid[6] != 4 /* md5WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does " "not match with certificate " "signatureAlgorithm (%lu)", - cert->signature.oid.oid[6]); + signature->oid.oid[6]); os_free(data); return -1; } @@ -1743,34 +1922,41 @@ skip_digest_oid: wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest", hdr.payload, hdr.length); - switch (cert->signature.oid.oid[6]) { + switch (signature->oid.oid[6]) { case 4: /* md5WithRSAEncryption */ - md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, - hash); + md5_vector(1, addr, len, hash); hash_len = 16; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)", hash, hash_len); break; case 5: /* sha-1WithRSAEncryption */ - sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, - hash); + sha1_vector(1, addr, len, hash); hash_len = 20; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)", hash, hash_len); break; case 11: /* sha256WithRSAEncryption */ - sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, - hash); + sha256_vector(1, addr, len, hash); hash_len = 32; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)", hash, hash_len); break; - case 2: /* md2WithRSAEncryption */ case 12: /* sha384WithRSAEncryption */ + sha384_vector(1, addr, len, hash); + hash_len = 48; + wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA384)", + hash, hash_len); + break; case 13: /* sha512WithRSAEncryption */ + sha512_vector(1, addr, len, hash); + hash_len = 64; + wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA512)", + hash, hash_len); + break; + case 2: /* md2WithRSAEncryption */ default: wpa_printf(MSG_INFO, "X509: Unsupported certificate signature " - "algorithm (%lu)", cert->signature.oid.oid[6]); + "algorithm (%lu)", signature->oid.oid[6]); os_free(data); return -1; } @@ -1852,6 +2038,7 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, os_get_time(&now); for (cert = chain, idx = 0; cert; cert = cert->next, idx++) { + cert->issuer_trusted = 0; x509_name_string(&cert->subject, buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf); @@ -1937,6 +2124,7 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, wpa_printf(MSG_DEBUG, "X509: Trusted certificate " "found to complete the chain"); + cert->issuer_trusted = 1; chain_trusted = 1; } } diff --git a/src/tls/x509v3.h b/src/tls/x509v3.h index 91a35baf92b18..7df8e2ab0870c 100644 --- a/src/tls/x509v3.h +++ b/src/tls/x509v3.h @@ -45,13 +45,18 @@ struct x509_name { struct asn1_oid rid; /* registeredID */ }; +#define X509_MAX_SERIAL_NUM_LEN 20 + struct x509_certificate { struct x509_certificate *next; enum { X509_CERT_V1 = 0, X509_CERT_V2 = 1, X509_CERT_V3 = 2 } version; - unsigned long serial_number; + u8 serial_number[X509_MAX_SERIAL_NUM_LEN]; + size_t serial_number_len; struct x509_algorithm_identifier signature; struct x509_name issuer; struct x509_name subject; + u8 *subject_dn; + size_t subject_dn_len; os_time_t not_before; os_time_t not_after; struct x509_algorithm_identifier public_key_alg; @@ -68,6 +73,7 @@ struct x509_certificate { #define X509_EXT_KEY_USAGE (1 << 2) #define X509_EXT_SUBJECT_ALT_NAME (1 << 3) #define X509_EXT_ISSUER_ALT_NAME (1 << 4) +#define X509_EXT_EXT_KEY_USAGE (1 << 5) /* BasicConstraints */ int ca; /* cA */ @@ -85,6 +91,13 @@ struct x509_certificate { #define X509_KEY_USAGE_ENCIPHER_ONLY (1 << 7) #define X509_KEY_USAGE_DECIPHER_ONLY (1 << 8) + /* ExtKeyUsage */ + unsigned long ext_key_usage; +#define X509_EXT_KEY_USAGE_ANY (1 << 0) +#define X509_EXT_KEY_USAGE_SERVER_AUTH (1 << 1) +#define X509_EXT_KEY_USAGE_CLIENT_AUTH (1 << 2) +#define X509_EXT_KEY_USAGE_OCSP (1 << 3) + /* * The DER format certificate follows struct x509_certificate. These * pointers point to that buffer. @@ -93,6 +106,11 @@ struct x509_certificate { size_t cert_len; const u8 *tbs_cert_start; size_t tbs_cert_len; + + /* Meta data used for certificate validation */ + unsigned int ocsp_good:1; + unsigned int ocsp_revoked:1; + unsigned int issuer_trusted:1; }; enum { @@ -106,10 +124,21 @@ enum { }; void x509_certificate_free(struct x509_certificate *cert); +int x509_parse_algorithm_identifier(const u8 *buf, size_t len, + struct x509_algorithm_identifier *id, + const u8 **next); +int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, + const u8 **next); +int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val); struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len); +void x509_free_name(struct x509_name *name); void x509_name_string(struct x509_name *name, char *buf, size_t len); int x509_name_compare(struct x509_name *a, struct x509_name *b); void x509_certificate_chain_free(struct x509_certificate *cert); +int x509_check_signature(struct x509_certificate *issuer, + struct x509_algorithm_identifier *signature, + const u8 *sign_value, size_t sign_value_len, + const u8 *signed_data, size_t signed_data_len); int x509_certificate_check_signature(struct x509_certificate *issuer, struct x509_certificate *cert); int x509_certificate_chain_validate(struct x509_certificate *trusted, @@ -120,4 +149,9 @@ x509_certificate_get_subject(struct x509_certificate *chain, struct x509_name *name); int x509_certificate_self_signed(struct x509_certificate *cert); +int x509_sha1_oid(struct asn1_oid *oid); +int x509_sha256_oid(struct asn1_oid *oid); +int x509_sha384_oid(struct asn1_oid *oid); +int x509_sha512_oid(struct asn1_oid *oid); + #endif /* X509V3_H */ diff --git a/src/utils/browser-android.c b/src/utils/browser-android.c index 9ce1a5cbeae10..71a165269cf6e 100644 --- a/src/utils/browser-android.c +++ b/src/utils/browser-android.c @@ -95,7 +95,7 @@ int hs20_web_browser(const char *url) if (pid == 0) { /* run the external command in the child process */ - char *argv[9]; + char *argv[7]; argv[0] = "browser-android"; argv[1] = "start"; @@ -103,9 +103,7 @@ int hs20_web_browser(const char *url) argv[3] = "android.intent.action.VIEW"; argv[4] = "-d"; argv[5] = (void *) url; - argv[6] = "-n"; - argv[7] = "com.android.browser/.BrowserActivity"; - argv[8] = NULL; + argv[6] = NULL; execv("/system/bin/am", argv); wpa_printf(MSG_ERROR, "execv: %s", strerror(errno)); diff --git a/src/utils/common.c b/src/utils/common.c index 660e9fc985d69..04a533a05902f 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -86,7 +86,7 @@ int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable) return -1; /* check for optional mask */ - if (*r == '\0' || isspace(*r)) { + if (*r == '\0' || isspace((unsigned char) *r)) { /* no mask specified, assume default */ os_memset(mask, 0xff, ETH_ALEN); } else if (maskable && *r == '/') { @@ -498,7 +498,7 @@ void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len) *txt++ = 't'; break; default: - if (data[i] >= 32 && data[i] <= 127) { + if (data[i] >= 32 && data[i] <= 126) { *txt++ = data[i]; } else { txt += os_snprintf(txt, end - txt, "\\x%02x", @@ -697,6 +697,29 @@ int is_hex(const u8 *data, size_t len) } +int has_ctrl_char(const u8 *data, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (data[i] < 32 || data[i] == 127) + return 1; + } + return 0; +} + + +int has_newline(const char *str) +{ + while (*str) { + if (*str == '\n' || *str == '\r') + return 1; + str++; + } + return 0; +} + + size_t merge_byte_arrays(u8 *res, size_t res_len, const u8 *src1, size_t src1_len, const u8 *src2, size_t src2_len) @@ -978,7 +1001,7 @@ int random_mac_addr_keep_oui(u8 *addr) * @delim: a string of delimiters * @last: a pointer to a character following the returned token * It has to be set to NULL for the first call and passed for any - * futher call. + * further call. * Returns: a pointer to token position in str or NULL * * This function is similar to str_token, but it can be used with both @@ -1123,3 +1146,57 @@ int is_ctrl_char(char c) { return c > 0 && c < 32; } + + +/** + * ssid_parse - Parse a string that contains SSID in hex or text format + * @buf: Input NULL terminated string that contains the SSID + * @ssid: Output SSID + * Returns: 0 on success, -1 otherwise + * + * The SSID has to be enclosed in double quotes for the text format or space + * or NULL terminated string of hex digits for the hex format. buf can include + * additional arguments after the SSID. + */ +int ssid_parse(const char *buf, struct wpa_ssid_value *ssid) +{ + char *tmp, *res, *end; + size_t len; + + ssid->ssid_len = 0; + + tmp = os_strdup(buf); + if (!tmp) + return -1; + + if (*tmp != '"') { + end = os_strchr(tmp, ' '); + if (end) + *end = '\0'; + } else { + end = os_strchr(tmp + 1, '"'); + if (!end) { + os_free(tmp); + return -1; + } + + end[1] = '\0'; + } + + res = wpa_config_parse_string(tmp, &len); + if (res && len <= SSID_MAX_LEN) { + ssid->ssid_len = len; + os_memcpy(ssid->ssid, res, len); + } + + os_free(tmp); + os_free(res); + + return ssid->ssid_len ? 0 : -1; +} + + +int str_starts(const char *str, const char *start) +{ + return os_strncmp(str, start, os_strlen(start)) == 0; +} diff --git a/src/utils/common.h b/src/utils/common.h index 0b9cc3d882090..77856774d215c 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -313,6 +313,9 @@ static inline void WPA_PUT_LE64(u8 *a, u64 val) #ifndef ETH_P_ALL #define ETH_P_ALL 0x0003 #endif +#ifndef ETH_P_IP +#define ETH_P_IP 0x0800 +#endif #ifndef ETH_P_80211_ENCAP #define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */ #endif @@ -416,6 +419,7 @@ void perror(const char *s); */ #ifdef __CHECKER__ #define __force __attribute__((force)) +#undef __bitwise #define __bitwise __attribute__((bitwise)) #else #define __force @@ -445,6 +449,13 @@ typedef u64 __bitwise le64; #endif /* __GNUC__ */ #endif /* __must_check */ +#define SSID_MAX_LEN 32 + +struct wpa_ssid_value { + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len; +}; + int hwaddr_aton(const char *txt, u8 *addr); int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable); int hwaddr_compact_aton(const char *txt, u8 *addr); @@ -461,6 +472,7 @@ int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, size_t len); int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask); +int ssid_parse(const char *buf, struct wpa_ssid_value *ssid); #ifdef CONFIG_NATIVE_WINDOWS void wpa_unicode2ascii_inplace(TCHAR *str); @@ -477,6 +489,8 @@ const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len); char * wpa_config_parse_string(const char *value, size_t *len); int is_hex(const u8 *data, size_t len); +int has_ctrl_char(const u8 *data, size_t len); +int has_newline(const char *str); size_t merge_byte_arrays(u8 *res, size_t res_len, const u8 *src1, size_t src1_len, const u8 *src2, size_t src2_len); @@ -536,6 +550,8 @@ size_t utf8_unescape(const char *inp, size_t in_size, char *outp, size_t out_size); int is_ctrl_char(char c); +int str_starts(const char *str, const char *start); + /* * gcc 4.4 ends up generating strict-aliasing warnings about some very common diff --git a/src/utils/edit_simple.c b/src/utils/edit_simple.c index 13173cb19361a..2ffd1a2a2b7ea 100644 --- a/src/utils/edit_simple.c +++ b/src/utils/edit_simple.c @@ -47,6 +47,12 @@ static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) return; } + if (c == '\b') { + if (cmdbuf_pos > 0) + cmdbuf_pos--; + return; + } + if (c >= 32 && c <= 255) { if (cmdbuf_pos < (int) sizeof(cmdbuf) - 1) { cmdbuf[cmdbuf_pos++] = c; diff --git a/src/utils/eloop.c b/src/utils/eloop.c index 8647229b8eb5f..436bc8c993389 100644 --- a/src/utils/eloop.c +++ b/src/utils/eloop.c @@ -18,7 +18,12 @@ #error Do not define both of poll and epoll #endif -#if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL) +#if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_KQUEUE) +#error Do not define both of poll and kqueue +#endif + +#if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL) && \ + !defined(CONFIG_ELOOP_KQUEUE) #define CONFIG_ELOOP_SELECT #endif @@ -30,6 +35,10 @@ #include <sys/epoll.h> #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE +#include <sys/event.h> +#endif /* CONFIG_ELOOP_KQUEUE */ + struct eloop_sock { int sock; void *eloop_data; @@ -75,13 +84,20 @@ struct eloop_data { struct pollfd *pollfds; struct pollfd **pollfds_map; #endif /* CONFIG_ELOOP_POLL */ +#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE) + int max_fd; + struct eloop_sock *fd_table; +#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */ #ifdef CONFIG_ELOOP_EPOLL int epollfd; int epoll_max_event_num; - int epoll_max_fd; - struct eloop_sock *epoll_table; struct epoll_event *epoll_events; #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + int kqueuefd; + int kqueue_nevents; + struct kevent *kqueue_events; +#endif /* CONFIG_ELOOP_KQUEUE */ struct eloop_sock_table readers; struct eloop_sock_table writers; struct eloop_sock_table exceptions; @@ -149,14 +165,24 @@ int eloop_init(void) #ifdef CONFIG_ELOOP_EPOLL eloop.epollfd = epoll_create1(0); if (eloop.epollfd < 0) { - wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s\n", + wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s", + __func__, strerror(errno)); + return -1; + } +#endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + eloop.kqueuefd = kqueue(); + if (eloop.kqueuefd < 0) { + wpa_printf(MSG_ERROR, "%s: kqueue failed: %s", __func__, strerror(errno)); return -1; } +#endif /* CONFIG_ELOOP_KQUEUE */ +#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE) eloop.readers.type = EVENT_TYPE_READ; eloop.writers.type = EVENT_TYPE_WRITE; eloop.exceptions.type = EVENT_TYPE_EXCEPTION; -#endif /* CONFIG_ELOOP_EPOLL */ +#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */ #ifdef WPA_TRACE signal(SIGSEGV, eloop_sigsegv_handler); #endif /* WPA_TRACE */ @@ -164,15 +190,80 @@ int eloop_init(void) } +#ifdef CONFIG_ELOOP_EPOLL +static int eloop_sock_queue(int sock, eloop_event_type type) +{ + struct epoll_event ev; + + os_memset(&ev, 0, sizeof(ev)); + switch (type) { + case EVENT_TYPE_READ: + ev.events = EPOLLIN; + break; + case EVENT_TYPE_WRITE: + ev.events = EPOLLOUT; + break; + /* + * Exceptions are always checked when using epoll, but I suppose it's + * possible that someone registered a socket *only* for exception + * handling. + */ + case EVENT_TYPE_EXCEPTION: + ev.events = EPOLLERR | EPOLLHUP; + break; + } + ev.data.fd = sock; + if (epoll_ctl(eloop.epollfd, EPOLL_CTL_ADD, sock, &ev) < 0) { + wpa_printf(MSG_ERROR, "%s: epoll_ctl(ADD) for fd=%d failed: %s", + __func__, sock, strerror(errno)); + return -1; + } + return 0; +} +#endif /* CONFIG_ELOOP_EPOLL */ + + +#ifdef CONFIG_ELOOP_KQUEUE +static int eloop_sock_queue(int sock, eloop_event_type type) +{ + int filter; + struct kevent ke; + + switch (type) { + case EVENT_TYPE_READ: + filter = EVFILT_READ; + break; + case EVENT_TYPE_WRITE: + filter = EVFILT_WRITE; + break; + default: + filter = 0; + } + EV_SET(&ke, sock, filter, 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)); + return -1; + } + return 0; +} +#endif /* CONFIG_ELOOP_KQUEUE */ + + static int eloop_sock_table_add_sock(struct eloop_sock_table *table, int sock, eloop_sock_handler handler, void *eloop_data, void *user_data) { #ifdef CONFIG_ELOOP_EPOLL + struct epoll_event *temp_events; +#endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + struct kevent *temp_events; +#endif /* CONFIG_ELOOP_EPOLL */ +#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE) struct eloop_sock *temp_table; - struct epoll_event ev, *temp_events; int next; -#endif /* CONFIG_ELOOP_EPOLL */ +#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */ struct eloop_sock *tmp; int new_max_sock; @@ -208,26 +299,28 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, eloop.pollfds = n; } #endif /* CONFIG_ELOOP_POLL */ -#ifdef CONFIG_ELOOP_EPOLL - if (new_max_sock >= eloop.epoll_max_fd) { - next = eloop.epoll_max_fd == 0 ? 16 : eloop.epoll_max_fd * 2; - temp_table = os_realloc_array(eloop.epoll_table, next, +#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; + temp_table = os_realloc_array(eloop.fd_table, next, sizeof(struct eloop_sock)); if (temp_table == NULL) return -1; - eloop.epoll_max_fd = next; - eloop.epoll_table = temp_table; + eloop.max_fd = next; + eloop.fd_table = temp_table; } +#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */ +#ifdef CONFIG_ELOOP_EPOLL if (eloop.count + 1 > eloop.epoll_max_event_num) { next = eloop.epoll_max_event_num == 0 ? 8 : eloop.epoll_max_event_num * 2; temp_events = os_realloc_array(eloop.epoll_events, next, sizeof(struct epoll_event)); if (temp_events == NULL) { - wpa_printf(MSG_ERROR, "%s: malloc for epoll failed. " - "%s\n", __func__, strerror(errno)); + wpa_printf(MSG_ERROR, "%s: malloc for epoll failed: %s", + __func__, strerror(errno)); return -1; } @@ -235,6 +328,22 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, eloop.epoll_events = temp_events; } #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + if (eloop.count + 1 > eloop.kqueue_nevents) { + next = eloop.kqueue_nevents == 0 ? 8 : eloop.kqueue_nevents * 2; + temp_events = os_malloc(next * sizeof(*temp_events)); + if (!temp_events) { + wpa_printf(MSG_ERROR, + "%s: malloc for kqueue failed: %s", + __func__, strerror(errno)); + return -1; + } + + os_free(eloop.kqueue_events); + eloop.kqueue_events = temp_events; + eloop.kqueue_nevents = next; + } +#endif /* CONFIG_ELOOP_KQUEUE */ eloop_trace_sock_remove_ref(table); tmp = os_realloc_array(table->table, table->count + 1, @@ -256,33 +365,12 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, table->changed = 1; eloop_trace_sock_add_ref(table); -#ifdef CONFIG_ELOOP_EPOLL - os_memset(&ev, 0, sizeof(ev)); - switch (table->type) { - case EVENT_TYPE_READ: - ev.events = EPOLLIN; - break; - case EVENT_TYPE_WRITE: - ev.events = EPOLLOUT; - break; - /* - * Exceptions are always checked when using epoll, but I suppose it's - * possible that someone registered a socket *only* for exception - * handling. - */ - case EVENT_TYPE_EXCEPTION: - ev.events = EPOLLERR | EPOLLHUP; - break; - } - ev.data.fd = sock; - if (epoll_ctl(eloop.epollfd, EPOLL_CTL_ADD, sock, &ev) < 0) { - wpa_printf(MSG_ERROR, "%s: epoll_ctl(ADD) for fd=%d " - "failed. %s\n", __func__, sock, strerror(errno)); +#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE) + if (eloop_sock_queue(sock, table->type) < 0) return -1; - } - os_memcpy(&eloop.epoll_table[sock], &table->table[table->count - 1], + os_memcpy(&eloop.fd_table[sock], &table->table[table->count - 1], sizeof(struct eloop_sock)); -#endif /* CONFIG_ELOOP_EPOLL */ +#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */ return 0; } @@ -290,6 +378,9 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, int sock) { +#ifdef CONFIG_ELOOP_KQUEUE + struct kevent ke; +#endif /* CONFIG_ELOOP_KQUEUE */ int i; if (table == NULL || table->table == NULL || table->count == 0) @@ -313,12 +404,21 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, eloop_trace_sock_add_ref(table); #ifdef CONFIG_ELOOP_EPOLL if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) { - wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d " - "failed. %s\n", __func__, sock, strerror(errno)); + wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d failed: %s", + __func__, sock, strerror(errno)); return; } - os_memset(&eloop.epoll_table[sock], 0, sizeof(struct eloop_sock)); + 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); + 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)); + return; + } + os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock)); +#endif /* CONFIG_ELOOP_KQUEUE */ } @@ -511,7 +611,7 @@ static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds) int i; for (i = 0; i < nfds; i++) { - table = &eloop.epoll_table[events[i].data.fd]; + table = &eloop.fd_table[events[i].data.fd]; if (table->handler == NULL) continue; table->handler(table->sock, table->eloop_data, @@ -525,6 +625,67 @@ static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds) #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + +static void eloop_sock_table_dispatch(struct kevent *events, int nfds) +{ + struct eloop_sock *table; + int i; + + for (i = 0; i < nfds; i++) { + table = &eloop.fd_table[events[i].ident]; + if (table->handler == NULL) + continue; + table->handler(table->sock, table->eloop_data, + table->user_data); + if (eloop.readers.changed || + eloop.writers.changed || + eloop.exceptions.changed) + break; + } +} + + +static int eloop_sock_table_requeue(struct eloop_sock_table *table) +{ + int i, r; + + r = 0; + for (i = 0; i < table->count && table->table; i++) { + if (eloop_sock_queue(table->table[i].sock, table->type) == -1) + r = -1; + } + return r; +} + +#endif /* CONFIG_ELOOP_KQUEUE */ + + +int eloop_sock_requeue(void) +{ + int r = 0; + +#ifdef CONFIG_ELOOP_KQUEUE + close(eloop.kqueuefd); + eloop.kqueuefd = kqueue(); + if (eloop.kqueuefd < 0) { + wpa_printf(MSG_ERROR, "%s: kqueue failed: %s", + __func__, strerror(errno)); + return -1; + } + + if (eloop_sock_table_requeue(&eloop.readers) < 0) + r = -1; + if (eloop_sock_table_requeue(&eloop.writers) < 0) + r = -1; + if (eloop_sock_table_requeue(&eloop.exceptions) < 0) + r = -1; +#endif /* CONFIG_ELOOP_KQUEUE */ + + return r; +} + + static void eloop_sock_table_destroy(struct eloop_sock_table *table) { if (table) { @@ -905,6 +1066,9 @@ void eloop_run(void) #ifdef CONFIG_ELOOP_EPOLL int timeout_ms = -1; #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + struct timespec ts; +#endif /* CONFIG_ELOOP_KQUEUE */ int res; struct os_reltime tv, now; @@ -949,6 +1113,10 @@ void eloop_run(void) _tv.tv_sec = tv.sec; _tv.tv_usec = tv.usec; #endif /* CONFIG_ELOOP_SELECT */ +#ifdef CONFIG_ELOOP_KQUEUE + ts.tv_sec = tv.sec; + ts.tv_nsec = tv.usec * 1000L; +#endif /* CONFIG_ELOOP_KQUEUE */ } #ifdef CONFIG_ELOOP_POLL @@ -974,6 +1142,15 @@ void eloop_run(void) eloop.count, timeout_ms); } #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + if (eloop.count == 0) { + res = 0; + } else { + res = kevent(eloop.kqueuefd, NULL, 0, + eloop.kqueue_events, eloop.kqueue_nevents, + timeout ? &ts : NULL); + } +#endif /* CONFIG_ELOOP_KQUEUE */ if (res < 0 && errno != EINTR && errno != 0) { wpa_printf(MSG_ERROR, "eloop: %s: %s", #ifdef CONFIG_ELOOP_POLL @@ -985,6 +1162,10 @@ void eloop_run(void) #ifdef CONFIG_ELOOP_EPOLL "epoll" #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + "kqueue" +#endif /* CONFIG_ELOOP_EKQUEUE */ + , strerror(errno)); goto out; } @@ -995,6 +1176,7 @@ void eloop_run(void) eloop_process_pending_signals(); + /* check if some registered timeouts have occurred */ timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, list); @@ -1040,6 +1222,9 @@ void eloop_run(void) #ifdef CONFIG_ELOOP_EPOLL eloop_sock_table_dispatch(eloop.epoll_events, res); #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + eloop_sock_table_dispatch(eloop.kqueue_events, res); +#endif /* CONFIG_ELOOP_KQUEUE */ } eloop.terminate = 0; @@ -1092,11 +1277,17 @@ void eloop_destroy(void) os_free(eloop.pollfds); os_free(eloop.pollfds_map); #endif /* CONFIG_ELOOP_POLL */ +#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE) + os_free(eloop.fd_table); +#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */ #ifdef CONFIG_ELOOP_EPOLL - os_free(eloop.epoll_table); os_free(eloop.epoll_events); close(eloop.epollfd); #endif /* CONFIG_ELOOP_EPOLL */ +#ifdef CONFIG_ELOOP_KQUEUE + os_free(eloop.kqueue_events); + close(eloop.kqueuefd); +#endif /* CONFIG_ELOOP_KQUEUE */ } @@ -1135,6 +1326,17 @@ void eloop_wait_for_read_sock(int sock) FD_SET(sock, &rfds); select(sock + 1, &rfds, NULL, NULL, NULL); #endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */ +#ifdef CONFIG_ELOOP_KQUEUE + int kfd; + struct kevent ke1, ke2; + + kfd = kqueue(); + if (kfd == -1) + return; + EV_SET(&ke1, sock, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, 0); + kevent(kfd, &ke1, 1, &ke2, 1, NULL); + close(kfd); +#endif /* CONFIG_ELOOP_KQUEUE */ } #ifdef CONFIG_ELOOP_SELECT diff --git a/src/utils/eloop.h b/src/utils/eloop.h index 07b8c0dc33520..97af16f0130aa 100644 --- a/src/utils/eloop.h +++ b/src/utils/eloop.h @@ -313,6 +313,14 @@ int eloop_register_signal_reconfig(eloop_signal_handler handler, void *user_data); /** + * eloop_sock_requeue - Requeue sockets + * + * Requeue sockets after forking because some implementations require this, + * such as epoll and kqueue. + */ +int eloop_sock_requeue(void); + +/** * eloop_run - Start the event loop * * Start the event loop and continue running as long as there are any diff --git a/src/utils/eloop_win.c b/src/utils/eloop_win.c index de47fb21837c2..9c8b12be8ad80 100644 --- a/src/utils/eloop_win.c +++ b/src/utils/eloop_win.c @@ -692,3 +692,9 @@ void eloop_wait_for_read_sock(int sock) WSAEventSelect(sock, event, 0); WSACloseEvent(event); } + + +int eloop_sock_requeue(void) +{ + return 0; +} diff --git a/src/utils/ext_password.c b/src/utils/ext_password.c index 06131197a3113..5615bd72a714d 100644 --- a/src/utils/ext_password.c +++ b/src/utils/ext_password.c @@ -16,10 +16,6 @@ #include "ext_password_i.h" -#ifdef CONFIG_EXT_PASSWORD_TEST -extern struct ext_password_backend ext_password_test; -#endif /* CONFIG_EXT_PASSWORD_TEST */ - static const struct ext_password_backend *backends[] = { #ifdef CONFIG_EXT_PASSWORD_TEST &ext_password_test, diff --git a/src/utils/ext_password_i.h b/src/utils/ext_password_i.h index 043e7312c62fb..948eaf5421b9e 100644 --- a/src/utils/ext_password_i.h +++ b/src/utils/ext_password_i.h @@ -20,4 +20,10 @@ struct ext_password_backend { struct wpabuf * ext_password_alloc(size_t len); +/* Available ext_password backends */ + +#ifdef CONFIG_EXT_PASSWORD_TEST +extern const struct ext_password_backend ext_password_test; +#endif /* CONFIG_EXT_PASSWORD_TEST */ + #endif /* EXT_PASSWORD_I_H */ diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c index 653eb541ab472..a06aae8d9b9d0 100644 --- a/src/utils/http_curl.c +++ b/src/utils/http_curl.c @@ -26,6 +26,9 @@ #include "common.h" #include "xml-utils.h" #include "http-utils.h" +#ifdef EAP_TLS_OPENSSL +#include "crypto/tls_openssl.h" +#endif /* EAP_TLS_OPENSSL */ struct http_ctx { @@ -421,6 +424,28 @@ ASN1_SEQUENCE(LogotypeExtn) = { IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn); +#ifdef OPENSSL_IS_BORINGSSL +#define sk_LogotypeInfo_num(st) \ +sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeInfo) *, (st))) +#define sk_LogotypeInfo_value(st, i) (LogotypeInfo *) \ +sk_value(CHECKED_CAST(_STACK *, const STACK_OF(LogotypeInfo) *, (st)), (i)) +#define sk_LogotypeImage_num(st) \ +sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeImage) *, (st))) +#define sk_LogotypeImage_value(st, i) (LogotypeImage *) \ +sk_value(CHECKED_CAST(_STACK *, const STACK_OF(LogotypeImage) *, (st)), (i)) +#define sk_LogotypeAudio_num(st) \ +sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeAudio) *, (st))) +#define sk_LogotypeAudio_value(st, i) (LogotypeAudio *) \ +sk_value(CHECK_CAST(_STACK *, const STACK_OF(LogotypeAudio) *, (st)), (i)) +#define sk_HashAlgAndValue_num(st) \ +sk_num(CHECKED_CAST(_STACK *, STACK_OF(HashAlgAndValue) *, (st))) +#define sk_HashAlgAndValue_value(st, i) (HashAlgAndValue *) \ +sk_value(CHECKED_CAST(_STACK *, const STACK_OF(HashAlgAndValue) *, (st)), (i)) +#define sk_ASN1_IA5STRING_num(st) \ +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 */ #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)) @@ -431,6 +456,7 @@ IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn); #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)) +#endif /* OPENSSL_IS_BORINGSSL */ static void add_logo(struct http_ctx *ctx, struct http_cert *hcert, @@ -618,13 +644,25 @@ static void i2r_LogotypeImageInfo(LogotypeImageInfo *info, BIO *out, int indent) } else { BIO_printf(out, "%*stype: default (1)\n", indent, ""); } + val = ASN1_INTEGER_get(info->fileSize); + BIO_printf(out, "%*sfileSize: %ld\n", indent, "", val); val = ASN1_INTEGER_get(info->xSize); BIO_printf(out, "%*sxSize: %ld\n", indent, "", val); val = ASN1_INTEGER_get(info->ySize); BIO_printf(out, "%*sySize: %ld\n", indent, "", val); if (info->resolution) { - BIO_printf(out, "%*sresolution\n", indent, ""); - /* TODO */ + BIO_printf(out, "%*sresolution [%d]\n", indent, "", + info->resolution->type); + switch (info->resolution->type) { + case 0: + val = ASN1_INTEGER_get(info->resolution->d.numBits); + BIO_printf(out, "%*snumBits: %ld\n", indent, "", val); + break; + case 1: + val = ASN1_INTEGER_get(info->resolution->d.tableSize); + BIO_printf(out, "%*stableSize: %ld\n", indent, "", val); + break; + } } if (info->language) { BIO_printf(out, "%*slanguage: ", indent, ""); @@ -981,6 +1019,26 @@ static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) if (depth == 0 && preverify_ok && validate_server_cert(ctx, cert) < 0) return 0; +#ifdef OPENSSL_IS_BORINGSSL + if (depth == 0 && ctx->ocsp != NO_OCSP && preverify_ok) { + enum ocsp_result res; + + res = check_ocsp_resp(ssl_ctx, ssl, cert, ctx->peer_issuer, + ctx->peer_issuer_issuer); + if (res == OCSP_REVOKED) { + preverify_ok = 0; + wpa_printf(MSG_INFO, "OCSP: certificate revoked"); + if (err == X509_V_OK) + X509_STORE_CTX_set_error( + x509_ctx, X509_V_ERR_CERT_REVOKED); + } else if (res != OCSP_GOOD && (ctx->ocsp == MANDATORY_OCSP)) { + preverify_ok = 0; + wpa_printf(MSG_INFO, + "OCSP: bad certificate status response"); + } + } +#endif /* OPENSSL_IS_BORINGSSL */ + if (!preverify_ok) ctx->last_err = "TLS validation failed"; @@ -1156,6 +1214,7 @@ static int ocsp_resp_cb(SSL *s, void *arg) wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", (ctx->ocsp == MANDATORY_OCSP) ? "" : " (OCSP not required)"); + OCSP_CERTID_free(id); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); if (ctx->ocsp == MANDATORY_OCSP) @@ -1163,6 +1222,7 @@ static int ocsp_resp_cb(SSL *s, void *arg) ctx->last_err = "Could not find current server certificate from OCSP response"; return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1; } + OCSP_CERTID_free(id); if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) { tls_show_errors(__func__, "OpenSSL: OCSP status times invalid"); @@ -1273,6 +1333,16 @@ 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 + /* 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 + * implementation within libcurl is not sufficient for the + * Hotspot 2.0 OSU needs, so cannot use this with OpenSSL. + */ + if (ctx->ocsp != NO_OCSP) + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L); +#endif /* OPENSSL_IS_BORINGSSL */ #endif /* EAP_TLS_OPENSSL */ } else { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); diff --git a/src/utils/module_tests.h b/src/utils/module_tests.h new file mode 100644 index 0000000000000..3bfe4ad026cce --- /dev/null +++ b/src/utils/module_tests.h @@ -0,0 +1,20 @@ +/* + * Module tests + * Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MODULE_TESTS_H +#define MODULE_TESTS_H + +int wpas_module_tests(void); +int hapd_module_tests(void); + +int utils_module_tests(void); +int wps_module_tests(void); +int common_module_tests(void); +int crypto_module_tests(void); + +#endif /* MODULE_TESTS_H */ diff --git a/src/utils/os.h b/src/utils/os.h index 9e496fb659783..e8f0b792738ab 100644 --- a/src/utils/os.h +++ b/src/utils/os.h @@ -657,6 +657,10 @@ int os_exec(const char *program, const char *arg, int wait_completion); #if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) #define TEST_FAIL() testing_test_fail() int testing_test_fail(void); +extern char wpa_trace_fail_func[256]; +extern unsigned int wpa_trace_fail_after; +extern char wpa_trace_test_fail_func[256]; +extern unsigned int wpa_trace_test_fail_after; #else #define TEST_FAIL() 0 #endif diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index ffa2e788b3db8..65c6fa412f207 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -372,6 +372,7 @@ void os_program_deinit(void) if (total) wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes", (unsigned long) total); + wpa_trace_deinit(); #endif /* WPA_TRACE */ } @@ -434,27 +435,23 @@ char * os_readfile(const char *name, size_t *len) int os_file_exists(const char *fname) { - FILE *f = fopen(fname, "rb"); - if (f == NULL) - return 0; - fclose(f); - return 1; + return access(fname, F_OK) == 0; } int os_fdatasync(FILE *stream) { if (!fflush(stream)) { -#ifndef __MACH__ +#ifdef __linux__ return fdatasync(fileno(stream)); -#else /* __MACH__ */ +#else /* !__linux__ */ #ifdef F_FULLFSYNC /* OS X does not implement fdatasync(). */ return fcntl(fileno(stream), F_FULLFSYNC); #else /* F_FULLFSYNC */ -#error Neither fdatasync nor F_FULLSYNC are defined + return fsync(fileno(stream)); #endif /* F_FULLFSYNC */ -#endif /* __MACH__ */ +#endif /* __linux__ */ } return -1; diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c index 6f5ea93962131..383ed3ddfeed5 100644 --- a/src/utils/pcsc_funcs.c +++ b/src/utils/pcsc_funcs.c @@ -11,7 +11,11 @@ */ #include "includes.h" +#ifdef __APPLE__ +#include <PCSC/winscard.h> +#else #include <winscard.h> +#endif #include "common.h" #include "pcsc_funcs.h" @@ -110,7 +114,11 @@ typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types; struct scard_data { SCARDCONTEXT ctx; SCARDHANDLE card; +#ifdef __APPLE__ + uint32_t protocol; +#else DWORD protocol; +#endif sim_types sim_type; int pin1_required; }; @@ -275,7 +283,7 @@ static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, pos++; if (pos >= end) return -1; - if ((pos + pos[0]) < end) + if (pos[0] < end - pos) end = pos + 1 + pos[0]; pos++; wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template", @@ -504,7 +512,12 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid, struct scard_data * scard_init(const char *reader) { long ret; - unsigned long len, pos; +#ifdef __APPLE__ + uint32_t len; +#else + unsigned long len; +#endif + unsigned long pos; struct scard_data *scard; #ifdef CONFIG_NATIVE_WINDOWS TCHAR *readers = NULL; @@ -605,7 +618,7 @@ struct scard_data * scard_init(const char *reader) readers = NULL; wpa_printf(MSG_DEBUG, "SCARD: card=0x%x active_protocol=%lu (%s)", - (unsigned int) scard->card, scard->protocol, + (unsigned int) scard->card, (unsigned long) scard->protocol, scard->protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1"); ret = SCardBeginTransaction(scard->card); @@ -764,7 +777,11 @@ static long scard_transmit(struct scard_data *scard, unsigned char *_recv, size_t *recv_len) { long ret; +#ifdef __APPLE__ + uint32_t rlen; +#else unsigned long rlen; +#endif wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send", _send, send_len); @@ -1385,7 +1402,7 @@ int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, end = buf + len; /* RES */ - if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) { + if (pos[0] > RES_MAX_LEN || pos[0] > end - pos) { wpa_printf(MSG_DEBUG, "SCARD: Invalid RES"); return -1; } @@ -1395,7 +1412,7 @@ int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len); /* CK */ - if (pos[0] != CK_LEN || pos + CK_LEN > end) { + if (pos[0] != CK_LEN || CK_LEN > end - pos) { wpa_printf(MSG_DEBUG, "SCARD: Invalid CK"); return -1; } @@ -1405,7 +1422,7 @@ int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN); /* IK */ - if (pos[0] != IK_LEN || pos + IK_LEN > end) { + if (pos[0] != IK_LEN || IK_LEN > end - pos) { wpa_printf(MSG_DEBUG, "SCARD: Invalid IK"); return -1; } diff --git a/src/utils/platform.h b/src/utils/platform.h index 46cfe785e1806..813987eb66064 100644 --- a/src/utils/platform.h +++ b/src/utils/platform.h @@ -15,7 +15,7 @@ \ __ptr->__val; \ }) -#define get_unaligned_le16(p) le16_to_cpu(get_unaligned((uint16_t *)(p))) -#define get_unaligned_le32(p) le32_to_cpu(get_unaligned((uint32_t *)(p))) +#define get_unaligned_le16(p) le16_to_cpu(get_unaligned((le16 *)(p))) +#define get_unaligned_le32(p) le32_to_cpu(get_unaligned((le32 *)(p))) #endif /* PLATFORM_H */ diff --git a/src/utils/radiotap.c b/src/utils/radiotap.c index c9a5023355922..71996eb7908f5 100644 --- a/src/utils/radiotap.c +++ b/src/utils/radiotap.c @@ -13,8 +13,8 @@ * * See COPYING for more details. */ -#include "radiotap_iter.h" #include "platform.h" +#include "radiotap_iter.h" /* function prototypes and related defs are in radiotap_iter.h */ diff --git a/src/utils/radiotap.h b/src/utils/radiotap.h index 0572e7c963da7..460af23d81246 100644 --- a/src/utils/radiotap.h +++ b/src/utils/radiotap.h @@ -65,12 +65,12 @@ struct ieee80211_radiotap_header { * new fields does not count. */ uint8_t it_pad; - uint16_t it_len; /* length of the whole + le16 it_len; /* length of the whole * header in bytes, including * it_version, it_pad, * it_len, and data fields. */ - uint32_t it_present; /* A bitmap telling which + le32 it_present; /* A bitmap telling which * fields are present. Set bit 31 * (0x80000000) to extend the * bitmap by another 32 bits. diff --git a/src/utils/radiotap_iter.h b/src/utils/radiotap_iter.h index b768c85baace5..6ea07e3b1b83f 100644 --- a/src/utils/radiotap_iter.h +++ b/src/utils/radiotap_iter.h @@ -67,7 +67,7 @@ struct ieee80211_radiotap_iterator { const struct ieee80211_radiotap_namespace *current_namespace; unsigned char *_arg, *_next_ns_data; - uint32_t *_next_bitmap; + le32 *_next_bitmap; unsigned char *this_arg; #ifdef RADIOTAP_SUPPORT_OVERRIDES diff --git a/src/utils/trace.c b/src/utils/trace.c index 8484d277d24b6..d72cf604f8e9b 100644 --- a/src/utils/trace.c +++ b/src/utils/trace.c @@ -366,4 +366,13 @@ void wpa_trace_check_ref(const void *addr) } } + +void wpa_trace_deinit(void) +{ +#ifdef WPA_TRACE_BFD + free(syms); + syms = NULL; +#endif /* WPA_TRACE_BFD */ +} + #endif /* WPA_TRACE */ diff --git a/src/utils/trace.h b/src/utils/trace.h index 43ed86c199786..d1636de077281 100644 --- a/src/utils/trace.h +++ b/src/utils/trace.h @@ -66,4 +66,6 @@ void wpa_trace_dump_funcname(const char *title, void *pc); #endif /* WPA_TRACE_BFD */ +void wpa_trace_deinit(void); + #endif /* TRACE_H */ diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c index 41511b9999a62..abdb79c9879c1 100644 --- a/src/utils/utils_module_tests.c +++ b/src/utils/utils_module_tests.c @@ -16,6 +16,7 @@ #include "utils/base64.h" #include "utils/ip_addr.h" #include "utils/eloop.h" +#include "utils/module_tests.h" struct printf_test_data { diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c index 61c0d5ce68c79..f7acf6b9f6984 100644 --- a/src/utils/wpa_debug.c +++ b/src/utils/wpa_debug.c @@ -148,7 +148,7 @@ int wpa_debug_open_linux_tracing(void) strtok_r(line, " ", &tmp2); tmp_path = strtok_r(NULL, " ", &tmp2); fstype = strtok_r(NULL, " ", &tmp2); - if (strcmp(fstype, "debugfs") == 0) { + if (fstype && strcmp(fstype, "debugfs") == 0) { path = tmp_path; break; } @@ -517,16 +517,18 @@ int wpa_debug_reopen_file(void) { #ifdef CONFIG_DEBUG_FILE int rv; - if (last_path) { - char *tmp = os_strdup(last_path); - wpa_debug_close_file(); - rv = wpa_debug_open_file(tmp); - os_free(tmp); - } else { - wpa_printf(MSG_ERROR, "Last-path was not set, cannot " - "re-open log file."); - rv = -1; - } + char *tmp; + + if (!last_path) + return 0; /* logfile not used */ + + tmp = os_strdup(last_path); + if (!tmp) + return -1; + + wpa_debug_close_file(); + rv = wpa_debug_open_file(tmp); + os_free(tmp); return rv; #else /* CONFIG_DEBUG_FILE */ return 0; diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c index 11e7323619de8..96cb25cc1764b 100644 --- a/src/utils/wpabuf.c +++ b/src/utils/wpabuf.c @@ -310,3 +310,33 @@ void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) wpabuf_overflow(buf, res); buf->used += res; } + + +/** + * wpabuf_parse_bin - Parse a null terminated string of binary data to a wpabuf + * @buf: Buffer with null terminated string (hexdump) of binary data + * Returns: wpabuf or %NULL on failure + * + * The string len must be a multiple of two and contain only hexadecimal digits. + */ +struct wpabuf * wpabuf_parse_bin(const char *buf) +{ + size_t len; + struct wpabuf *ret; + + len = os_strlen(buf); + if (len & 0x01) + return NULL; + len /= 2; + + ret = wpabuf_alloc(len); + if (ret == NULL) + return NULL; + + if (hexstr2bin(buf, wpabuf_put(ret, len), len)) { + wpabuf_free(ret); + return NULL; + } + + return ret; +} diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h index c3ef1bae3667a..01da41b324d66 100644 --- a/src/utils/wpabuf.h +++ b/src/utils/wpabuf.h @@ -37,6 +37,7 @@ void * wpabuf_put(struct wpabuf *buf, size_t len); struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b); struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len); void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3); +struct wpabuf * wpabuf_parse_bin(const char *buf); /** @@ -81,7 +82,7 @@ static inline const void * wpabuf_head(const struct wpabuf *buf) static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf) { - return wpabuf_head(buf); + return (const u8 *) wpabuf_head(buf); } /** @@ -96,42 +97,42 @@ static inline void * wpabuf_mhead(struct wpabuf *buf) static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf) { - return wpabuf_mhead(buf); + return (u8 *) wpabuf_mhead(buf); } static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data) { - u8 *pos = wpabuf_put(buf, 1); + u8 *pos = (u8 *) wpabuf_put(buf, 1); *pos = data; } static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data) { - u8 *pos = wpabuf_put(buf, 2); + u8 *pos = (u8 *) wpabuf_put(buf, 2); WPA_PUT_LE16(pos, data); } static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data) { - u8 *pos = wpabuf_put(buf, 4); + u8 *pos = (u8 *) wpabuf_put(buf, 4); WPA_PUT_LE32(pos, data); } static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data) { - u8 *pos = wpabuf_put(buf, 2); + u8 *pos = (u8 *) wpabuf_put(buf, 2); WPA_PUT_BE16(pos, data); } static inline void wpabuf_put_be24(struct wpabuf *buf, u32 data) { - u8 *pos = wpabuf_put(buf, 3); + u8 *pos = (u8 *) wpabuf_put(buf, 3); WPA_PUT_BE24(pos, data); } static inline void wpabuf_put_be32(struct wpabuf *buf, u32 data) { - u8 *pos = wpabuf_put(buf, 4); + u8 *pos = (u8 *) wpabuf_put(buf, 4); WPA_PUT_BE32(pos, data); } diff --git a/src/utils/xml_libxml2.c b/src/utils/xml_libxml2.c index c92839461dad4..7b6d2764b0ed2 100644 --- a/src/utils/xml_libxml2.c +++ b/src/utils/xml_libxml2.c @@ -212,6 +212,8 @@ char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node) xmlDocSetRootElement(doc, n); xmlDocDumpFormatMemory(doc, &buf, &bufsiz, 0); xmlFreeDoc(doc); + if (!buf) + return NULL; pos = (char *) buf; if (strncmp(pos, "<?xml", 5) == 0) { pos = strchr(pos, '>'); diff --git a/src/wps/wps.c b/src/wps/wps.c index fbaf85aabab4a..fade6b6905dc8 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -19,6 +19,10 @@ int wps_version_number = 0x20; int wps_testing_dummy_cred = 0; int wps_corrupt_pkhash = 0; +int wps_force_auth_types_in_use = 0; +u16 wps_force_auth_types = 0; +int wps_force_encr_types_in_use = 0; +u16 wps_force_encr_types = 0; #endif /* CONFIG_WPS_TESTING */ @@ -170,7 +174,7 @@ void wps_deinit(struct wps_data *data) } else if (data->registrar) wps_registrar_unlock_pin(data->wps->registrar, data->uuid_e); - wpabuf_free(data->dh_privkey); + wpabuf_clear_free(data->dh_privkey); wpabuf_free(data->dh_pubkey_e); wpabuf_free(data->dh_pubkey_r); wpabuf_free(data->last_msg); diff --git a/src/wps/wps.h b/src/wps/wps.h index 2c91d1678c157..2505d2d9f246d 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - * Copyright (c) 2007-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2016, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -664,6 +664,16 @@ struct wps_context { u16 encr_types; /** + * encr_types_rsn - Enabled encryption types for RSN (WPS_ENCR_*) + */ + u16 encr_types_rsn; + + /** + * encr_types_wpa - Enabled encryption types for WPA (WPS_ENCR_*) + */ + u16 encr_types_wpa; + + /** * auth_types - Authentication types (bit field of WPS_AUTH_*) */ u16 auth_types; @@ -827,7 +837,7 @@ int wps_build_credential_wrap(struct wpabuf *msg, unsigned int wps_pin_checksum(unsigned int pin); unsigned int wps_pin_valid(unsigned int pin); -unsigned int wps_generate_pin(void); +int wps_generate_pin(unsigned int *pin); int wps_pin_str_valid(const char *pin); void wps_free_pending_msgs(struct upnp_pending_message *msgs); diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c index b689357a280a6..770f5e90cbde0 100644 --- a/src/wps/wps_attr_build.c +++ b/src/wps/wps_attr_build.c @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - attribute building - * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2016, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -20,10 +20,10 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg) { - struct wpabuf *pubkey; + struct wpabuf *pubkey = NULL; wpa_printf(MSG_DEBUG, "WPS: * Public Key"); - wpabuf_free(wps->dh_privkey); + wpabuf_clear_free(wps->dh_privkey); wps->dh_privkey = NULL; if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey && wps->wps->dh_ctx) { @@ -298,7 +298,16 @@ int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg) auth_types &= ~WPS_AUTH_WPA; auth_types &= ~WPS_AUTH_WPA2; auth_types &= ~WPS_AUTH_SHARED; - wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags"); +#ifdef CONFIG_WPS_TESTING + if (wps_force_auth_types_in_use) { + wpa_printf(MSG_DEBUG, + "WPS: Testing - replace auth type 0x%x with 0x%x", + auth_types, wps_force_auth_types); + auth_types = wps_force_auth_types; + } +#endif /* CONFIG_WPS_TESTING */ + wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags (0x%x)", + auth_types); wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS); wpabuf_put_be16(msg, 2); wpabuf_put_be16(msg, auth_types); @@ -310,7 +319,16 @@ int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg) { u16 encr_types = WPS_ENCR_TYPES; encr_types &= ~WPS_ENCR_WEP; - wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags"); +#ifdef CONFIG_WPS_TESTING + if (wps_force_encr_types_in_use) { + wpa_printf(MSG_DEBUG, + "WPS: Testing - replace encr type 0x%x with 0x%x", + encr_types, wps_force_encr_types); + encr_types = wps_force_encr_types; + } +#endif /* CONFIG_WPS_TESTING */ + wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags (0x%x)", + encr_types); wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS); wpabuf_put_be16(msg, 2); wpabuf_put_be16(msg, encr_types); @@ -395,7 +413,8 @@ int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id, dev_pw_id); addr[0] = wpabuf_head(pubkey); hash_len = wpabuf_len(pubkey); - sha256_vector(1, addr, &hash_len, pubkey_hash); + if (sha256_vector(1, addr, &hash_len, pubkey_hash) < 0) + return -1; #ifdef CONFIG_WPS_TESTING if (wps_corrupt_pkhash) { wpa_hexdump(MSG_DEBUG, "WPS: Real Public Key Hash", diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c index 11a967ba0ef18..756d57e876c55 100644 --- a/src/wps/wps_attr_parse.c +++ b/src/wps/wps_attr_parse.c @@ -83,10 +83,10 @@ static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos, const u8 *end = pos + len; u8 id, elen; - while (pos + 2 <= end) { + while (end - pos >= 2) { id = *pos++; elen = *pos++; - if (pos + elen > end) + if (elen > end - pos) break; if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0) return -1; diff --git a/src/wps/wps_attr_process.c b/src/wps/wps_attr_process.c index eadb22fe2e78b..e8c4579309ab3 100644 --- a/src/wps/wps_attr_process.c +++ b/src/wps/wps_attr_process.c @@ -229,6 +229,16 @@ static int wps_workaround_cred_key(struct wps_credential *cred) cred->key_len--; #endif /* CONFIG_WPS_STRICT */ } + + + if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) && + (cred->key_len < 8 || has_ctrl_char(cred->key, cred->key_len))) { + wpa_printf(MSG_INFO, "WPS: Reject credential with invalid WPA/WPA2-Personal passphrase"); + wpa_hexdump_ascii_key(MSG_INFO, "WPS: Network Key", + cred->key, cred->key_len); + return -1; + } + return 0; } diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c index 88f85fe83f057..2e3472177de9e 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -90,7 +90,7 @@ int wps_derive_keys(struct wps_data *wps) } /* Own DH private key is not needed anymore */ - wpabuf_free(wps->dh_privkey); + wpabuf_clear_free(wps->dh_privkey); wps->dh_privkey = NULL; wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared); @@ -100,7 +100,7 @@ int wps_derive_keys(struct wps_data *wps) len[0] = wpabuf_len(dh_shared); sha256_vector(1, addr, len, dhkey); wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey)); - wpabuf_free(dh_shared); + wpabuf_clear_free(dh_shared); /* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */ addr[0] = wps->nonce_e; @@ -129,23 +129,26 @@ int wps_derive_keys(struct wps_data *wps) } -void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, - size_t dev_passwd_len) +int wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, + size_t dev_passwd_len) { u8 hash[SHA256_MAC_LEN]; - hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd, - (dev_passwd_len + 1) / 2, hash); + if (hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd, + (dev_passwd_len + 1) / 2, hash) < 0) + return -1; os_memcpy(wps->psk1, hash, WPS_PSK_LEN); - hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, - dev_passwd + (dev_passwd_len + 1) / 2, - dev_passwd_len / 2, hash); + if (hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, + dev_passwd + (dev_passwd_len + 1) / 2, + dev_passwd_len / 2, hash) < 0) + return -1; os_memcpy(wps->psk2, hash, WPS_PSK_LEN); wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password", dev_passwd, dev_passwd_len); wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN); wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN); + return 0; } @@ -173,7 +176,7 @@ struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size); if (aes_128_cbc_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted), wpabuf_len(decrypted))) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); return NULL; } @@ -184,14 +187,14 @@ struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, pad = *pos; if (pad > wpabuf_len(decrypted)) { wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad value"); - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); return NULL; } for (i = 0; i < pad; i++) { if (*pos-- != pad) { wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad " "string"); - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); return NULL; } } @@ -235,20 +238,18 @@ unsigned int wps_pin_valid(unsigned int pin) * wps_generate_pin - Generate a random PIN * Returns: Eight digit PIN (i.e., including the checksum digit) */ -unsigned int wps_generate_pin(void) +int wps_generate_pin(unsigned int *pin) { unsigned int val; /* Generate seven random digits for the PIN */ - if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) { - struct os_time now; - os_get_time(&now); - val = os_random() ^ now.sec ^ now.usec; - } + if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) + return -1; val %= 10000000; /* Append checksum digit */ - return val * 10 + wps_pin_checksum(val); + *pin = val * 10 + wps_pin_checksum(val); + return 0; } @@ -375,7 +376,7 @@ struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band, wps_build_mac_addr(plain, wps->dev.mac_addr) || wps_build_wfa_ext(plain, 0, NULL, 0)) { os_free(data.new_psk); - wpabuf_free(plain); + wpabuf_clear_free(plain); return NULL; } @@ -423,7 +424,7 @@ struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id, wps_build_wfa_ext(data, 0, NULL, 0)) { wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password " "token"); - wpabuf_free(data); + wpabuf_clear_free(data); return NULL; } @@ -660,7 +661,7 @@ int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey) wpabuf_free(*pubkey); *pubkey = pub; - wpabuf_free(*privkey); + wpabuf_clear_free(*privkey); *privkey = priv; return 0; @@ -691,7 +692,7 @@ struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey, } *id = 0x10 + val % 0xfff0; - wpabuf_free(*dev_pw); + wpabuf_clear_free(*dev_pw); *dev_pw = pw; return wps_nfc_token_build(ndef, *id, *pubkey, *dev_pw); diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h index a23b979d2e3c7..301864da433d0 100644 --- a/src/wps/wps_defs.h +++ b/src/wps/wps_defs.h @@ -14,6 +14,10 @@ extern int wps_version_number; extern int wps_testing_dummy_cred; extern int wps_corrupt_pkhash; +extern int wps_force_auth_types_in_use; +extern u16 wps_force_auth_types; +extern int wps_force_encr_types_in_use; +extern u16 wps_force_encr_types; #define WPS_VERSION wps_version_number #else /* CONFIG_WPS_TESTING */ diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c index 9321b721abd75..417507740d7aa 100644 --- a/src/wps/wps_enrollee.c +++ b/src/wps/wps_enrollee.c @@ -173,7 +173,8 @@ static struct wpabuf * wps_build_m3(struct wps_data *wps) wpa_printf(MSG_DEBUG, "WPS: No Device Password available"); return NULL; } - wps_derive_psk(wps, wps->dev_password, wps->dev_password_len); + if (wps_derive_psk(wps, wps->dev_password, wps->dev_password_len) < 0) + return NULL; if (wps->wps->ap && random_pool_ready() != 1) { wpa_printf(MSG_INFO, @@ -224,11 +225,11 @@ static struct wpabuf * wps_build_m5(struct wps_data *wps) wps_build_encr_settings(wps, msg, plain) || wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { - wpabuf_free(plain); + wpabuf_clear_free(plain); wpabuf_free(msg); return NULL; } - wpabuf_free(plain); + wpabuf_clear_free(plain); wps->state = RECV_M6; return msg; @@ -394,11 +395,11 @@ static struct wpabuf * wps_build_m7(struct wps_data *wps) wps_build_encr_settings(wps, msg, plain) || wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { - wpabuf_free(plain); + wpabuf_clear_free(plain); wpabuf_free(msg); return NULL; } - wpabuf_free(plain); + wpabuf_clear_free(plain); if (wps->wps->ap && wps->wps->registrar) { /* @@ -1007,11 +1008,11 @@ static enum wps_process_res wps_process_m2(struct wps_data *wps, eattr.key_wrap_auth) || wps_process_creds(wps, eattr.cred, eattr.cred_len, eattr.num_cred, attr->version2 != NULL)) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = WPS_MSG_DONE; return WPS_CONTINUE; @@ -1112,7 +1113,7 @@ static enum wps_process_res wps_process_m4(struct wps_data *wps, } if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } @@ -1122,11 +1123,11 @@ static enum wps_process_res wps_process_m4(struct wps_data *wps, if (wps_parse_msg(decrypted, &eattr) < 0 || wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || wps_process_r_snonce1(wps, eattr.r_snonce1)) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_M5; return WPS_CONTINUE; @@ -1165,7 +1166,7 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps, } if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } @@ -1175,11 +1176,11 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps, if (wps_parse_msg(decrypted, &eattr) < 0 || wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || wps_process_r_snonce2(wps, eattr.r_snonce2)) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); if (wps->wps->ap) wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS, @@ -1236,7 +1237,7 @@ static enum wps_process_res wps_process_m8(struct wps_data *wps, if (wps_validate_m8_encr(decrypted, wps->wps->ap, attr->version2 != NULL) < 0) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } @@ -1249,11 +1250,11 @@ static enum wps_process_res wps_process_m8(struct wps_data *wps, eattr.num_cred, attr->version2 != NULL) || wps_process_ap_settings_e(wps, &eattr, decrypted, attr->version2 != NULL)) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = WPS_MSG_DONE; return WPS_CONTINUE; diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h index f7154f8734bbd..fe0c60bd120bf 100644 --- a/src/wps/wps_i.h +++ b/src/wps/wps_i.h @@ -132,8 +132,8 @@ struct wps_data { void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len, const char *label, u8 *res, size_t res_len); int wps_derive_keys(struct wps_data *wps); -void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, - size_t dev_passwd_len); +int wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, + size_t dev_passwd_len); struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, size_t encr_len); void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg, diff --git a/src/wps/wps_module_tests.c b/src/wps/wps_module_tests.c index 350630768be4b..23bed4b36e011 100644 --- a/src/wps/wps_module_tests.c +++ b/src/wps/wps_module_tests.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/module_tests.h" #include "wps_attr_parse.h" struct wps_attr_parse_test { @@ -17,7 +18,7 @@ struct wps_attr_parse_test { int extra; }; -const struct wps_attr_parse_test wps_attr_parse_test_cases[] = { +static const struct wps_attr_parse_test wps_attr_parse_test_cases[] = { /* Empty message */ { "", 0, 0 }, /* Truncated attribute header */ diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 4ca3a42d4c738..fac8bd837f2fd 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - Registrar - * Copyright (c) 2008-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2016, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -703,7 +703,7 @@ void wps_registrar_deinit(struct wps_registrar *reg) eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); wps_registrar_flush(reg); - wpabuf_free(reg->extra_cred); + wpabuf_clear_free(reg->extra_cred); os_free(reg); } @@ -1577,13 +1577,13 @@ int wps_build_credential_wrap(struct wpabuf *msg, if (wbuf == NULL) return -1; if (wps_build_credential(wbuf, cred)) { - wpabuf_free(wbuf); + wpabuf_clear_free(wbuf); return -1; } wpabuf_put_be16(msg, ATTR_CRED); wpabuf_put_be16(msg, wpabuf_len(wbuf)); wpabuf_put_buf(msg, wbuf); - wpabuf_free(wbuf); + wpabuf_clear_free(wbuf); return 0; } @@ -1606,6 +1606,9 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) wps->cred.ssid_len = wps->wps->ssid_len; /* Select the best authentication and encryption type */ + wpa_printf(MSG_DEBUG, + "WPS: Own auth types 0x%x - masked Enrollee auth types 0x%x", + wps->wps->auth_types, wps->auth_type); if (wps->auth_type & WPS_AUTH_WPA2PSK) wps->auth_type = WPS_AUTH_WPA2PSK; else if (wps->auth_type & WPS_AUTH_WPAPSK) @@ -1619,6 +1622,14 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) } wps->cred.auth_type = wps->auth_type; + wpa_printf(MSG_DEBUG, + "WPS: Own encr types 0x%x (rsn: 0x%x, wpa: 0x%x) - masked Enrollee encr types 0x%x", + wps->wps->encr_types, wps->wps->encr_types_rsn, + wps->wps->encr_types_wpa, wps->encr_type); + if (wps->wps->ap && wps->auth_type == WPS_AUTH_WPA2PSK) + wps->encr_type &= wps->wps->encr_types_rsn; + else if (wps->wps->ap && wps->auth_type == WPS_AUTH_WPAPSK) + wps->encr_type &= wps->wps->encr_types_wpa; if (wps->auth_type == WPS_AUTH_WPA2PSK || wps->auth_type == WPS_AUTH_WPAPSK) { if (wps->encr_type & WPS_ENCR_AES) @@ -1740,14 +1751,14 @@ use_provided: return -1; if (wps_build_credential(cred, &wps->cred)) { - wpabuf_free(cred); + wpabuf_clear_free(cred); return -1; } wpabuf_put_be16(msg, ATTR_CRED); wpabuf_put_be16(msg, wpabuf_len(cred)); wpabuf_put_buf(msg, cred); - wpabuf_free(cred); + wpabuf_clear_free(cred); skip_cred_build: if (wps->wps->registrar->extra_cred) { @@ -1785,7 +1796,7 @@ static struct wpabuf * wps_build_ap_cred(struct wps_data *wps) } if (wps_build_ap_settings(wps, plain)) { - wpabuf_free(plain); + wpabuf_clear_free(plain); wpabuf_free(msg); return NULL; } @@ -1793,7 +1804,7 @@ static struct wpabuf * wps_build_ap_cred(struct wps_data *wps) wpabuf_put_be16(msg, ATTR_CRED); wpabuf_put_be16(msg, wpabuf_len(plain)); wpabuf_put_buf(msg, plain); - wpabuf_free(plain); + wpabuf_clear_free(plain); return msg; } @@ -1853,10 +1864,10 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps) wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain)) { wpabuf_free(msg); - wpabuf_free(plain); + wpabuf_clear_free(plain); return NULL; } - wpabuf_free(plain); + wpabuf_clear_free(plain); config_in_m2 = 1; } #endif /* CONFIG_WPS_NFC */ @@ -1917,7 +1928,8 @@ static struct wpabuf * wps_build_m4(struct wps_data *wps) wpa_printf(MSG_DEBUG, "WPS: Building Message M4"); - wps_derive_psk(wps, wps->dev_password, wps->dev_password_len); + if (wps_derive_psk(wps, wps->dev_password, wps->dev_password_len) < 0) + return NULL; plain = wpabuf_alloc(200); if (plain == NULL) @@ -1938,11 +1950,11 @@ static struct wpabuf * wps_build_m4(struct wps_data *wps) wps_build_encr_settings(wps, msg, plain) || wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { - wpabuf_free(plain); + wpabuf_clear_free(plain); wpabuf_free(msg); return NULL; } - wpabuf_free(plain); + wpabuf_clear_free(plain); wps->state = RECV_M5; return msg; @@ -1973,11 +1985,11 @@ static struct wpabuf * wps_build_m6(struct wps_data *wps) wps_build_encr_settings(wps, msg, plain) || wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { - wpabuf_free(plain); + wpabuf_clear_free(plain); wpabuf_free(msg); return NULL; } - wpabuf_free(plain); + wpabuf_clear_free(plain); wps->wps_pin_revealed = 1; wps->state = RECV_M7; @@ -2010,11 +2022,11 @@ static struct wpabuf * wps_build_m8(struct wps_data *wps) wps_build_encr_settings(wps, msg, plain) || wps_build_wfa_ext(msg, 0, NULL, 0) || wps_build_authenticator(wps, msg)) { - wpabuf_free(plain); - wpabuf_free(msg); + wpabuf_clear_free(plain); + wpabuf_clear_free(msg); return NULL; } - wpabuf_free(plain); + wpabuf_clear_free(plain); wps->state = RECV_DONE; return msg; @@ -2343,6 +2355,23 @@ static int wps_process_auth_type_flags(struct wps_data *wps, const u8 *auth) wpa_printf(MSG_DEBUG, "WPS: Enrollee Authentication Type flags 0x%x", auth_types); +#ifdef WPS_WORKAROUNDS + /* + * Some deployed implementations seem to advertise incorrect information + * in this attribute. A value of 0x1b (WPA2 + WPA + WPAPSK + OPEN, but + * no WPA2PSK) has been reported to be used. Add WPA2PSK to the list to + * avoid issues with building Credentials that do not use the strongest + * actually supported authentication option (that device does support + * WPA2PSK even when it does not claim it here). + */ + if ((auth_types & + (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) == + (WPS_AUTH_WPA2 | WPS_AUTH_WPAPSK)) { + wpa_printf(MSG_DEBUG, + "WPS: Workaround - assume Enrollee supports WPA2PSK based on claimed WPA2 support"); + auth_types |= WPS_AUTH_WPA2PSK; + } +#endif /* WPS_WORKAROUNDS */ wps->auth_type = wps->wps->auth_types & auth_types; if (wps->auth_type == 0) { wpa_printf(MSG_DEBUG, "WPS: No match in supported " @@ -2757,7 +2786,7 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps, } if (wps_validate_m5_encr(decrypted, attr->version2 != NULL) < 0) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } @@ -2767,11 +2796,11 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps, if (wps_parse_msg(decrypted, &eattr) < 0 || wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || wps_process_e_snonce1(wps, eattr.e_snonce1)) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_M6; return WPS_CONTINUE; @@ -2909,7 +2938,7 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps, if (wps_validate_m7_encr(decrypted, wps->wps->ap || wps->er, attr->version2 != NULL) < 0) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } @@ -2920,12 +2949,12 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps, wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) || wps_process_e_snonce2(wps, eattr.e_snonce2) || wps_process_ap_settings_r(wps, &eattr)) { - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } - wpabuf_free(decrypted); + wpabuf_clear_free(decrypted); wps->state = SEND_M8; return WPS_CONTINUE; diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index 44318e0942520..0c458c6adef9d 100644 --- a/src/wps/wps_upnp.c +++ b/src/wps/wps_upnp.c @@ -1082,6 +1082,7 @@ upnp_wps_get_iface(struct upnp_wps_device_sm *sm, void *priv) void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv) { struct upnp_wps_device_interface *iface; + struct upnp_wps_peer *peer; if (!sm) return; @@ -1102,8 +1103,13 @@ void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv) iface->wps->registrar); dl_list_del(&iface->list); - if (iface->peer.wps) - wps_deinit(iface->peer.wps); + while ((peer = dl_list_first(&iface->peers, struct upnp_wps_peer, + list))) { + if (peer->wps) + wps_deinit(peer->wps); + dl_list_del(&peer->list); + os_free(peer); + } os_free(iface->ctx->ap_pin); os_free(iface->ctx); os_free(iface); @@ -1141,6 +1147,7 @@ upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps, } wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface); + dl_list_init(&iface->peers); iface->ctx = ctx; iface->wps = wps; iface->priv = priv; diff --git a/src/wps/wps_upnp.h b/src/wps/wps_upnp.h index 87b7ab14160b7..b6f6df5ec5192 100644 --- a/src/wps/wps_upnp.h +++ b/src/wps/wps_upnp.h @@ -11,11 +11,14 @@ #ifndef WPS_UPNP_H #define WPS_UPNP_H +#include "utils/list.h" + struct upnp_wps_device_sm; struct wps_context; struct wps_data; struct upnp_wps_peer { + struct dl_list list; struct wps_data *wps; }; diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h index f289fe685ac78..6a7c627253e30 100644 --- a/src/wps/wps_upnp_i.h +++ b/src/wps/wps_upnp_i.h @@ -109,8 +109,7 @@ struct upnp_wps_device_interface { struct wps_context *wps; void *priv; - /* FIX: maintain separate structures for each UPnP peer */ - struct upnp_wps_peer peer; + struct dl_list peers; /* active UPnP peer sessions */ }; /* diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c index 968fc03f92e7c..a685ce4c3c0fa 100644 --- a/src/wps/wps_upnp_ssdp.c +++ b/src/wps/wps_upnp_ssdp.c @@ -100,12 +100,6 @@ static int line_length(const char *l) } -static int str_starts(const char *str, const char *start) -{ - return os_strncmp(str, start, os_strlen(start)) == 0; -} - - /*************************************************************************** * Advertisements. * These are multicast to the world to tell them we are here. diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c index d5b0b5b26e9d5..7548e8432a68f 100644 --- a/src/wps/wps_upnp_web.c +++ b/src/wps/wps_upnp_web.c @@ -300,7 +300,8 @@ static void http_put_empty(struct wpabuf *buf, enum http_reply_code code) * would appear to be required (given that we will be closing it!). */ static void web_connection_parse_get(struct upnp_wps_device_sm *sm, - struct http_request *hreq, char *filename) + struct http_request *hreq, + const char *filename) { struct wpabuf *buf; /* output buffer, allocated */ char *put_length_here; @@ -409,6 +410,15 @@ send_buf: } +static void wps_upnp_peer_del(struct upnp_wps_peer *peer) +{ + dl_list_del(&peer->list); + if (peer->wps) + wps_deinit(peer->wps); + os_free(peer); +} + + static enum http_reply_code web_process_get_device_info(struct upnp_wps_device_sm *sm, struct wpabuf **reply, const char **replyname) @@ -426,7 +436,9 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm, if (!iface || iface->ctx->ap_pin == NULL) return HTTP_INTERNAL_SERVER_ERROR; - peer = &iface->peer; + peer = os_zalloc(sizeof(*peer)); + if (!peer) + return HTTP_INTERNAL_SERVER_ERROR; /* * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS @@ -436,9 +448,6 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm, * registration. */ - if (peer->wps) - wps_deinit(peer->wps); - os_memset(&cfg, 0, sizeof(cfg)); cfg.wps = iface->wps; cfg.pin = (u8 *) iface->ctx->ap_pin; @@ -455,8 +464,22 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm, *reply = NULL; if (*reply == NULL) { wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo"); + os_free(peer); return HTTP_INTERNAL_SERVER_ERROR; } + + if (dl_list_len(&iface->peers) > 3) { + struct upnp_wps_peer *old; + + old = dl_list_first(&iface->peers, struct upnp_wps_peer, list); + if (old) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Drop oldest active session"); + wps_upnp_peer_del(old); + } + } + dl_list_add_tail(&iface->peers, &peer->list); + /* TODO: Could schedule a timeout to free the entry */ + *replyname = name; return HTTP_OK; } @@ -472,6 +495,8 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data, enum wps_process_res res; enum wsc_op_code op_code; struct upnp_wps_device_interface *iface; + struct wps_parse_attr attr; + struct upnp_wps_peer *tmp, *peer; iface = dl_list_first(&sm->interfaces, struct upnp_wps_device_interface, list); @@ -487,11 +512,56 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data, msg = xml_get_base64_item(data, "NewInMessage", &ret); if (msg == NULL) return ret; - res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg); - if (res == WPS_FAILURE) + + if (wps_parse_msg(msg, &attr)) { + wpa_printf(MSG_DEBUG, + "WPS UPnP: Could not parse PutMessage - NewInMessage"); + wpabuf_free(msg); + return HTTP_BAD_REQUEST; + } + + /* Find a matching active peer session */ + peer = NULL; + dl_list_for_each(tmp, &iface->peers, struct upnp_wps_peer, list) { + if (!tmp->wps) + continue; + if (attr.enrollee_nonce && + os_memcmp(tmp->wps->nonce_e, attr.enrollee_nonce, + WPS_NONCE_LEN) != 0) + continue; /* Enrollee nonce mismatch */ + if (attr.msg_type && + *attr.msg_type != WPS_M2 && + *attr.msg_type != WPS_M2D && + attr.registrar_nonce && + os_memcmp(tmp->wps->nonce_r, attr.registrar_nonce, + WPS_NONCE_LEN) != 0) + continue; /* Registrar nonce mismatch */ + peer = tmp; + break; + } + if (!peer) { + /* + Try to use the first entry in case message could work with + * it. The actual handler function will reject this, if needed. + * This maintains older behavior where only a single peer entry + * was supported. + */ + peer = dl_list_first(&iface->peers, struct upnp_wps_peer, list); + } + if (!peer || !peer->wps) { + wpa_printf(MSG_DEBUG, "WPS UPnP: No active peer entry found"); + wpabuf_free(msg); + return HTTP_BAD_REQUEST; + } + + res = wps_process_msg(peer->wps, WSC_UPnP, msg); + if (res == WPS_FAILURE) { *reply = NULL; - else - *reply = wps_get_msg(iface->peer.wps, &op_code); + wpa_printf(MSG_DEBUG, "WPS UPnP: Drop active peer session"); + wps_upnp_peer_del(peer); + } else { + *reply = wps_get_msg(peer->wps, &op_code); + } wpabuf_free(msg); if (*reply == NULL) return HTTP_INTERNAL_SERVER_ERROR; |