diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index 702d9aa000..140bcb8169 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -4,6 +4,7 @@ set(srcs "port/os_xtensa.c" "src/ap/ieee802_1x.c" "src/ap/wpa_auth.c" "src/ap/wpa_auth_ie.c" + "src/ap/sta_info.c" "src/common/sae.c" "src/common/wpa_common.c" "src/utils/bitfield.c" @@ -24,6 +25,7 @@ set(srcs "port/os_xtensa.c" "src/crypto/md4-internal.c" "src/crypto/sha1-tprf.c" "src/eap_common/eap_wsc_common.c" + "src/common/ieee802_11_common.c" "src/eap_peer/chap.c" "src/eap_peer/eap.c" "src/eap_peer/eap_common.c" @@ -54,7 +56,6 @@ set(srcs "port/os_xtensa.c" "src/wps/wps_common.c" "src/wps/wps_dev_attr.c" "src/wps/wps_enrollee.c") -# "src/wps/wps_registrar.c") set(esp_srcs "esp_supplicant/src/esp_wpa2.c" "esp_supplicant/src/esp_wpa_main.c" @@ -176,8 +177,23 @@ else() set(dpp_src "") endif() -idf_component_register(SRCS "${srcs}" ${esp_srcs} "${tls_src}" "${roaming_src}" - "${crypto_src}" "${mbo_src}" "${dpp_src}" +if(CONFIG_WPA_WPS_SOFTAP_REGISTRAR) + set(wps_registrar_src + "src/ap/wps_hostapd.c" + "src/eap_server/eap_server.c" + "src/eap_server/eap_server_methods.c" + "src/eap_server/eap_server_wsc.c" + "src/ap/eap_user_db.c" + "src/eapol_auth/eapol_auth_sm.c" + "src/eap_server/eap_server_identity.c" + "esp_supplicant/src/esp_hostpad_wps.c" + "src/wps/wps_registrar.c") +else() + set(wps_registrar_src "") +endif() + +idf_component_register(SRCS "${srcs}" "${esp_srcs}" "${tls_src}" "${roaming_src}" + "${crypto_src}" "${mbo_src}" "${dpp_src}" "${wps_registrar_src}" INCLUDE_DIRS include port/include esp_supplicant/include PRIV_INCLUDE_DIRS src src/utils esp_supplicant/src src/crypto PRIV_REQUIRES mbedtls esp_timer) @@ -193,14 +209,14 @@ target_compile_definitions(${COMPONENT_LIB} PRIVATE EAP_TLS EAP_PEAP USE_WPA2_TASK - CONFIG_WPS2 - CONFIG_WPS_PIN + CONFIG_WPS USE_WPS_TASK ESPRESSIF_USE ESP32_WORKAROUND CONFIG_ECC CONFIG_IEEE80211W CONFIG_SHA256 + CONFIG_NO_RADIUS ) if(CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE) @@ -242,4 +258,7 @@ if(CONFIG_WPA_MBEDTLS_CRYPTO) else() target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_CRYPTO_INTERNAL) endif() +if(CONFIG_WPA_WPS_SOFTAP_REGISTRAR) + target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_WPS_REGISTRAR) +endif() set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY LINK_INTERFACE_MULTIPLICITY 3) diff --git a/components/wpa_supplicant/Kconfig b/components/wpa_supplicant/Kconfig index 5501c47906..f4a3fdf410 100644 --- a/components/wpa_supplicant/Kconfig +++ b/components/wpa_supplicant/Kconfig @@ -105,9 +105,16 @@ menu "Supplicant" Select this option to enable WiFi Easy Connect Support. config WPA_11R_SUPPORT - bool "Enable 802.11R (Fast Transition)" + bool "Enable 802.11R (Fast Transition) Support" default n help Select this option to enable WiFi Fast Transition Support. + config WPA_WPS_SOFTAP_REGISTRAR + bool "Add WPS Registrar support in SoftAP mode" + depends on ESP_WIFI_SOFTAP_SUPPORT + default n + help + Select this option to enable WPS registrar support in softAP mode. + endmenu diff --git a/components/wpa_supplicant/esp_supplicant/include/esp_wps.h b/components/wpa_supplicant/esp_supplicant/include/esp_wps.h index ef885eb802..3b5af6a031 100644 --- a/components/wpa_supplicant/esp_supplicant/include/esp_wps.h +++ b/components/wpa_supplicant/esp_supplicant/include/esp_wps.h @@ -25,13 +25,6 @@ extern "C" { * @{ */ -/** \defgroup WPS_APIs WPS APIs - * @brief ESP32 WPS APIs - * - * WPS can only be used when ESP32 station is enabled. - * - */ - /** @addtogroup WPS_APIs * @{ */ @@ -59,9 +52,11 @@ typedef struct { char device_name[WPS_MAX_DEVICE_NAME_LEN]; /*!< Device name, null-terminated string. The default device name is used if the string is empty */ } wps_factory_information_t; +#define PIN_LEN 9 typedef struct { wps_type_t wps_type; wps_factory_information_t factory_info; + char pin[PIN_LEN]; } esp_wps_config_t; #define WPS_CONFIG_INIT_DEFAULT(type) { \ @@ -70,14 +65,15 @@ typedef struct { ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_STR(manufacturer, "ESPRESSIF") \ ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_STR(model_number, "ESP32") \ ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_STR(model_name, "ESPRESSIF IOT") \ - ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_STR(device_name, "ESP STATION") \ - } \ + ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_STR(device_name, "ESP DEVICE") \ + }, \ + ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_STR(pin, "00000000") \ } /** * @brief Enable Wi-Fi WPS function. * - * @attention WPS can only be used when ESP32 station is enabled. + * @attention WPS can only be used when station is enabled. * * @param wps_type_t wps_type : WPS type, so far only WPS_TYPE_PBC and WPS_TYPE_PIN is supported * @@ -103,7 +99,7 @@ esp_err_t esp_wifi_wps_disable(void); /** * @brief WPS starts to work. * - * @attention WPS can only be used when ESP32 station is enabled. + * @attention WPS can only be used when station is enabled. * * @param timeout_ms : maximum blocking time before API return. * - 0 : non-blocking @@ -118,6 +114,47 @@ esp_err_t esp_wifi_wps_disable(void); */ esp_err_t esp_wifi_wps_start(int timeout_ms); +/** + * @brief Enable Wi-Fi AP WPS function. + * + * @attention WPS can only be used when softAP is enabled. + * + * @param wps_type_t wps_type : WPS type, so far only WPS_TYPE_PBC and WPS_TYPE_PIN is supported + * + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_WPS_TYPE : wps type is invalid + * - ESP_ERR_WIFI_WPS_MODE : wifi is not in station mode or sniffer mode is on + * - ESP_FAIL : wps initialization fails + */ +esp_err_t esp_wifi_ap_wps_enable(const esp_wps_config_t *config); + +/** + * @brief Disable Wi-Fi SoftAP WPS function and release resource it taken. + * + * @param null + * + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_WPS_MODE : wifi is not in station mode or sniffer mode is on + */ +esp_err_t esp_wifi_ap_wps_disable(void); + +/** + * @brief WPS starts to work. + * + * @attention WPS can only be used when softAP is enabled. + * + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_WPS_TYPE : wps type is invalid + * - ESP_ERR_WIFI_WPS_MODE : wifi is not in station mode or sniffer mode is on + * - ESP_ERR_WIFI_WPS_SM : wps state machine is not initialized + * - ESP_FAIL : wps initialization fails + */ +esp_err_t esp_wifi_ap_wps_start(const unsigned char *pin); + + /** * @} */ diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_common.c b/components/wpa_supplicant/esp_supplicant/src/esp_common.c index 93d5e0ae73..89fbb02cf7 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_common.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_common.c @@ -866,6 +866,7 @@ void esp_set_scan_ie(void) { } void esp_set_assoc_ie(uint8_t *bssid, const u8 *ies, size_t ies_len, bool mdie) { } int esp_supplicant_common_init(struct wpa_funcs *wpa_cb) { + wpa_cb->wpa_sta_rx_mgmt = NULL; return 0; } void esp_supplicant_common_deinit(void) { } diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_hostap.c b/components/wpa_supplicant/esp_supplicant/src/esp_hostap.c index d0d37731bd..1b3a6db832 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_hostap.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_hostap.c @@ -18,12 +18,18 @@ #include "esp_wifi_driver.h" #include "esp_wifi_types.h" +struct hostapd_data *global_hapd; + +struct hostapd_data *hostapd_get_hapd_data(void) +{ + return global_hapd; +} + void *hostap_init(void) { struct wifi_ssid *ssid = esp_wifi_ap_get_prof_ap_ssid_internal(); struct hostapd_data *hapd = NULL; struct wpa_auth_config *auth_conf; - u8 mac[6]; u16 spp_attrubute = 0; u8 pairwise_cipher; wifi_pmf_config_t pmf_cfg; @@ -40,6 +46,7 @@ void *hostap_init(void) os_free(hapd); return NULL; } + hapd->conf->max_num_sta = MAX_STA_COUNT; auth_conf = (struct wpa_auth_config *)os_zalloc(sizeof(struct wpa_auth_config)); @@ -122,11 +129,12 @@ void *hostap_init(void) hapd->conf->ap_max_inactivity = 5 * 60; hostapd_setup_wpa_psk(hapd->conf); - esp_wifi_get_macaddr_internal(WIFI_IF_AP, mac); + esp_wifi_get_macaddr_internal(WIFI_IF_AP, hapd->own_addr); - hapd->wpa_auth = wpa_init(mac, auth_conf, NULL); + hapd->wpa_auth = wpa_init(hapd->own_addr, auth_conf, NULL); esp_wifi_set_appie_internal(WIFI_APPIE_WPA, hapd->wpa_auth->wpa_ie, (uint16_t)hapd->wpa_auth->wpa_ie_len, 0); os_free(auth_conf); + global_hapd = hapd; return (void *)hapd; } @@ -161,6 +169,7 @@ bool hostap_deinit(void *data) os_free(hapd); esp_wifi_unset_appie_internal(WIFI_APPIE_WPA); + global_hapd = NULL; return true; } diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_hostpad_wps.c b/components/wpa_supplicant/esp_supplicant/src/esp_hostpad_wps.c new file mode 100644 index 0000000000..72344af8fd --- /dev/null +++ b/components/wpa_supplicant/esp_supplicant/src/esp_hostpad_wps.c @@ -0,0 +1,254 @@ +/* + * SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utils/common.h" + +#include "rsn_supp/wpa.h" +#include "common/eapol_common.h" +#include "utils/wpa_debug.h" +#include "common/ieee802_11_defs.h" +#include "wps/wps_i.h" +#include "wps/wps_dev_attr.h" +#include "eap_peer/eap_defs.h" +#include "eap_peer/eap_common.h" +#include "esp_wifi_driver.h" +#include "esp_event.h" +#include "esp_wifi.h" +#include "esp_err.h" +#include "esp_private/wifi.h" +#include "esp_wps.h" +#include "esp_wps_i.h" +#include "eap_common/eap_wsc_common.h" +#include "esp_wpas_glue.h" +#include "ap/hostapd.h" +#include "ap/ap_config.h" +#include "ap/wps_hostapd.h" + +extern struct wps_sm *gWpsSm; +extern void *s_wps_api_lock; +extern void *s_wps_api_sem; +extern bool s_wps_enabled; + +static int wifi_ap_wps_init(void) +{ + struct wps_sm *sm = NULL; + uint8_t mac[ETH_ALEN]; + struct wps_config cfg = {0}; + + if (gWpsSm) { + goto _out; + } + + wpa_printf(MSG_DEBUG, "wifi wps init"); + + gWpsSm = os_zalloc(sizeof(struct wps_sm)); /* alloc Wps_sm */ + if (!gWpsSm) { + goto _out; + } + + sm = gWpsSm; + + esp_wifi_get_macaddr_internal(WIFI_IF_AP, mac); + os_memcpy(sm->ownaddr, mac, ETH_ALEN); + + sm->identity_len = WSC_ID_REGISTRAR_LEN; + os_memcpy(sm->identity, WSC_ID_REGISTRAR, sm->identity_len); + + sm->wps_ctx = os_zalloc(sizeof(struct wps_context)); /* alloc wps_ctx */ + if (!sm->wps_ctx) { + goto _err; + } + + if (wps_dev_init() != 0) { + goto _err; + } + + cfg.registrar = 1; + cfg.wps = sm->wps_ctx; + + wps_init_cfg_pin(&cfg); + os_memcpy(cfg.wps->uuid, sm->uuid, WPS_UUID_LEN); + if ((sm->wps = wps_init(&cfg)) == NULL) { /* alloc wps_data */ + goto _err; + } + + hostapd_init_wps(hostapd_get_hapd_data(), sm->wps, sm->wps_ctx); + return ESP_OK; + +_err: + if (sm->dev) { + wps_dev_deinit(sm->dev); + sm->dev = NULL; + } + if (sm->wps_ctx) { + os_free(sm->wps_ctx); + sm->wps_ctx = NULL; + } + if (sm->wps) { + wps_deinit(sm->wps); + sm->wps = NULL; + } + os_free(gWpsSm); + gWpsSm = NULL; + return ESP_FAIL; +_out: + return ESP_FAIL; +} + +int wifi_ap_wps_deinit(void) +{ + struct wps_sm *sm = gWpsSm; + + hostapd_deinit_wps(hostapd_get_hapd_data()); + if (gWpsSm == NULL) { + return ESP_FAIL; + } + + if (sm->dev) { + wps_dev_deinit(sm->dev); + sm->dev = NULL; + } + if (sm->wps_ctx) { + os_free(sm->wps_ctx); + sm->wps_ctx = NULL; + } + if (sm->wps) { + wps_deinit(sm->wps); + sm->wps = NULL; + } + os_free(gWpsSm); + gWpsSm = NULL; + + return ESP_OK; +} + +int wifi_ap_wps_enable_internal(const esp_wps_config_t *config) +{ + int ret = 0; + + wpa_printf(MSG_DEBUG, "ESP WPS crypto initialize!"); + if (config->wps_type == WPS_TYPE_DISABLE) { + wpa_printf(MSG_ERROR, "wps enable: invalid wps type"); + return ESP_ERR_WIFI_WPS_TYPE; + } + + wpa_printf(MSG_DEBUG, "Set factory information."); + ret = wps_set_factory_info(config); + if (ret != 0) { + return ret; + } + + wpa_printf(MSG_INFO, "wifi_wps_enable\n"); + + wps_set_type(config->wps_type); + wps_set_status(WPS_STATUS_DISABLE); + + ret = wifi_ap_wps_init(); + + if (ret != 0) { + wps_set_type(WPS_STATUS_DISABLE); + wps_set_status(WPS_STATUS_DISABLE); + return ESP_FAIL; + } + + return ESP_OK; +} + +int esp_wifi_ap_wps_enable(const esp_wps_config_t *config) +{ + int ret; + wifi_mode_t mode = WIFI_MODE_NULL; + + ret = esp_wifi_get_mode(&mode); + if (mode != WIFI_MODE_AP) { + return ESP_ERR_WIFI_MODE; + } + + API_MUTEX_TAKE(); + if (s_wps_enabled) { + API_MUTEX_GIVE(); + wpa_printf(MSG_DEBUG, "wps enable: already enabled"); + return ESP_OK; + } + + ret = wifi_ap_wps_enable_internal(config); + s_wps_enabled = true; + API_MUTEX_GIVE(); + return ret; +} + +int esp_wifi_ap_wps_disable(void) +{ + int ret = 0; + wifi_mode_t mode = WIFI_MODE_NULL; + + ret = esp_wifi_get_mode(&mode); + if (mode != WIFI_MODE_AP) { + return ESP_ERR_WIFI_MODE; + } + + API_MUTEX_TAKE(); + + if (!s_wps_enabled) { + wpa_printf(MSG_DEBUG, "wps disable: already disabled"); + API_MUTEX_GIVE(); + return ESP_OK; + } + + wpa_printf(MSG_INFO, "wifi_wps_disable"); + wps_set_status(WPS_STATUS_DISABLE); + wps_set_type(WPS_TYPE_DISABLE); + + wifi_ap_wps_deinit(); + + if (ESP_OK != ret) { + wpa_printf(MSG_ERROR, "wps disable: failed to disable wps, ret=%d", ret); + } + + s_wps_enabled = false; + API_MUTEX_GIVE(); + return ESP_OK; +} + +int esp_wifi_ap_wps_start(const unsigned char *pin) +{ + wifi_mode_t mode = WIFI_MODE_NULL; + + esp_wifi_get_mode(&mode); + if (mode != WIFI_MODE_AP) { + wpa_printf(MSG_ERROR, "wps start: mode=%d is not AP", mode); + return ESP_ERR_WIFI_MODE; + } + + API_MUTEX_TAKE(); + + if (!s_wps_enabled) { + wpa_printf(MSG_ERROR, "wps start: wps not enabled"); + API_MUTEX_GIVE(); + return ESP_ERR_WIFI_WPS_SM; + } + + if (wps_get_type() == WPS_TYPE_DISABLE || (wps_get_status() != WPS_STATUS_DISABLE && wps_get_status() != WPS_STATUS_SCANNING)) { + API_MUTEX_GIVE(); + return ESP_ERR_WIFI_WPS_TYPE; + } + + if (esp_wifi_get_user_init_flag_internal() == 0) { + API_MUTEX_GIVE(); + return ESP_ERR_WIFI_STATE; + } + + /* TODO ideally SoftAP mode should also do a single scan in PBC mode + * however softAP scanning is not available at the moment */ + wps_set_status(WPS_STATUS_PENDING); + if (wps_get_type() == WPS_TYPE_PBC) { + hostapd_wps_button_pushed(hostapd_get_hapd_data(), NULL); + } else if (wps_get_type() == WPS_TYPE_PIN) { + hostapd_wps_add_pin(hostapd_get_hapd_data(), pin); + } + API_MUTEX_GIVE(); + return ESP_OK; +} diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c b/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c index 8d8856cebf..f8e8f3bc18 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c @@ -11,6 +11,7 @@ #include "rsn_supp/wpa_i.h" #include "common/eapol_common.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" #include "rsn_supp/wpa_ie.h" #include "ap/wpa_auth.h" #include "ap/wpa_auth_i.h" @@ -30,6 +31,13 @@ #include "esp_wpa2.h" #include "esp_common_i.h" +#include "esp_wps.h" +#include "eap_server/eap.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "ap/ieee802_1x.h" +#include "ap/sta_info.h" +#include "wps/wps_defs.h" + void wpa_install_key(enum wpa_alg alg, u8 *addr, int key_idx, int set_tx, u8 *seq, size_t seq_len, u8 *key, size_t key_len, enum key_flag key_flag) { @@ -134,16 +142,24 @@ uint8_t *wpa_ap_get_wpa_ie(uint8_t *ie_len) return hapd->wpa_auth->wpa_ie; } -bool wpa_ap_rx_eapol(void *hapd_data, void *sm_data, u8 *data, size_t data_len) +bool wpa_ap_rx_eapol(void *hapd_data, void *sm_data, u8 *data, size_t data_len) { struct hostapd_data *hapd = (struct hostapd_data *)hapd_data; - struct wpa_state_machine *sm = (struct wpa_state_machine *)sm_data; - - if (!hapd || !sm) { + struct sta_info *sta = (struct sta_info *)sm_data; + if (!hapd || !sta) { + wpa_printf(MSG_DEBUG, "hapd=%p sta=%p", hapd, sta); return false; } +#ifdef CONFIG_WPS_REGISTRAR + int wps_type = esp_wifi_get_wps_type_internal(); - wpa_receive(hapd->wpa_auth, sm, data, data_len); + if ((wps_type == WPS_TYPE_PBC) || + (wps_type == WPS_TYPE_PIN)) { + ieee802_1x_receive(hapd, sta->addr, data, data_len); + return true; + } +#endif + wpa_receive(hapd->wpa_auth, sta->wpa_sm, data, data_len); return true; } @@ -226,6 +242,60 @@ static void wpa_sta_disconnected_cb(uint8_t reason_code) } } +#ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT + +#ifdef CONFIG_WPS_REGISTRAR +static int check_n_add_wps_sta(struct hostapd_data *hapd, struct sta_info *sta_info, u8 *ies, u8 ies_len, bool *pmf_enable) +{ + struct wpabuf *wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, WPS_DEV_OUI_WFA); + int wps_type = esp_wifi_get_wps_type_internal(); + + /* Condition for this, WPS is running and WPS IEs are part of assoc req */ + if (!wps_ie || (wps_type == WPS_TYPE_DISABLE)) { + return 0; + } + + sta_info->wps_ie = wps_ie; + sta_info->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta_info); + + if (sta_info->eapol_sm) { + wpa_printf(MSG_DEBUG, "considering station " MACSTR " for WPS", MAC2STR(sta_info->addr)); + return 1; + } + + return 0; +} +#endif + +static bool hostap_sta_join(void **sm, u8 *bssid, u8 *wpa_ie, u8 wpa_ie_len, bool *pmf_enable) +{ + struct sta_info *sta_info; + struct hostapd_data *hapd = hostapd_get_hapd_data(); + + if (!hapd) { + return 0; + } + sta_info = ap_sta_add(hapd, bssid); + if (!sta_info) { + wpa_printf(MSG_ERROR, "failed to add station " MACSTR, MAC2STR(bssid)); + return 0; + } +#ifdef CONFIG_WPS_REGISTRAR + if (check_n_add_wps_sta(hapd, sta_info, wpa_ie, wpa_ie_len, pmf_enable)) { + *sm = sta_info; + return true; + } +#endif + if (wpa_ap_join(sm, bssid, wpa_ie, wpa_ie_len, pmf_enable)) { + sta_info->wpa_sm = *sm; + *sm = sta_info; + return true; + } + + return false; +} +#endif + int esp_supplicant_init(void) { int ret = ESP_OK; @@ -244,7 +314,7 @@ int esp_supplicant_init(void) wpa_cb->wpa_sta_in_4way_handshake = wpa_sta_in_4way_handshake; #ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT - wpa_cb->wpa_ap_join = wpa_ap_join; + wpa_cb->wpa_ap_join = hostap_sta_join; wpa_cb->wpa_ap_remove = wpa_ap_remove; wpa_cb->wpa_ap_get_wpa_ie = wpa_ap_get_wpa_ie; wpa_cb->wpa_ap_rx_eapol = wpa_ap_rx_eapol; diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.c b/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.c index aeb7bb8c6c..e37b78cec1 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.c @@ -73,6 +73,28 @@ int wpa_ether_send(void *ctx, const u8 *dest, u16 proto, return ESP_OK; } +int hostapd_send_eapol(const u8 *source, const u8 *sta_addr, + const u8 *data, size_t data_len) +{ + void *buffer = os_malloc(data_len + sizeof(struct l2_ethhdr)); + struct l2_ethhdr *eth = buffer; + + if (!buffer){ + wpa_printf( MSG_DEBUG, "send_eapol, buffer=%p\n", buffer); + return -1; + } + + memcpy(eth->h_dest, sta_addr, ETH_ALEN); + memcpy(eth->h_source, source, ETH_ALEN); + eth->h_proto = host_to_be16(ETH_P_EAPOL); + + memcpy((char *)buffer + sizeof(struct l2_ethhdr), data, data_len); + esp_wifi_internal_tx(WIFI_IF_AP, buffer, sizeof(struct l2_ethhdr) + data_len); + os_free(buffer); + return 0; + +} + u8 *wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type, const void *data, u16 data_len, size_t *msg_len, void **data_pos) diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.h b/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.h index 7d702314dd..220a01c633 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpas_glue.h @@ -31,4 +31,6 @@ void wpa_free_eapol(u8 *buffer); int wpa_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *data, size_t data_len); +int hostapd_send_eapol(const u8 *source, const u8 *sta_addr, + const u8 *data, size_t data_len); #endif /* WPAS_GLUE_H */ diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wps.c b/components/wpa_supplicant/esp_supplicant/src/esp_wps.c index 3e040fe694..3adf996e6f 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wps.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wps.c @@ -31,25 +31,12 @@ #include "eap_common/eap_wsc_common.h" #include "esp_wpas_glue.h" -#define API_MUTEX_TAKE() do {\ - if (!s_wps_api_lock) {\ - s_wps_api_lock = xSemaphoreCreateRecursiveMutex();\ - if (!s_wps_api_lock) {\ - wpa_printf(MSG_ERROR, "wps api lock create failed");\ - return ESP_ERR_NO_MEM;\ - }\ - }\ - xSemaphoreTakeRecursive(s_wps_api_lock, portMAX_DELAY);\ -} while(0) - -#define API_MUTEX_GIVE() xSemaphoreGiveRecursive(s_wps_api_lock) -#define DATA_MUTEX_TAKE() xSemaphoreTakeRecursive(s_wps_data_lock, portMAX_DELAY) -#define DATA_MUTEX_GIVE() xSemaphoreGiveRecursive(s_wps_data_lock) - -#define WPS_ADDR_LEN 6 +void *s_wps_api_lock = NULL; /* Used in WPS public API only, never be freed */ +void *s_wps_api_sem = NULL; /* Sync semaphore used between WPS publi API caller task and WPS task */ +bool s_wps_enabled = false; #ifdef USE_WPS_TASK struct wps_rx_param { - u8 sa[WPS_ADDR_LEN]; + u8 sa[ETH_ALEN]; u8 *buf; int len; STAILQ_ENTRY(wps_rx_param) bqentry; @@ -63,11 +50,8 @@ typedef struct { static TaskHandle_t s_wps_task_hdl = NULL; static void *s_wps_queue = NULL; -static void *s_wps_api_lock = NULL; /* Used in WPS public API only, never be freed */ -static void *s_wps_api_sem = NULL; /* Sync semaphore used between WPS publi API caller task and WPS task */ static void *s_wps_data_lock = NULL; static void *s_wps_task_create_sem = NULL; -static bool s_wps_enabled = false; static uint8_t s_wps_sig_cnt[SIG_WPS_NUM] = {0}; #endif @@ -89,31 +73,6 @@ void wps_add_discard_ap(u8 *bssid); struct wps_sm *gWpsSm = NULL; static wps_factory_information_t *s_factory_info = NULL; -#ifdef CONFIG_WPS_TESTING -int wps_version_number = 0x20; -int wps_testing_dummy_cred = 0; -#endif /* CONFIG_WPS_TESTING */ - -int wps_get_type(void) -{ - return esp_wifi_get_wps_type_internal(); -} - -int wps_set_type(uint32_t type) -{ - return esp_wifi_set_wps_type_internal(type); -} - -int wps_get_status(void) -{ - return esp_wifi_get_wps_status_internal(); -} - -int wps_set_status(uint32_t status) -{ - return esp_wifi_set_wps_status_internal(status); -} - static void wps_rxq_init(void) { DATA_MUTEX_TAKE(); @@ -395,7 +354,7 @@ wps_parse_scan_result(struct wps_scan_ie *scan) } if (!scan->rsn && !scan->wpa && (scan->capinfo & WLAN_CAPABILITY_PRIVACY)) { - wpa_printf(MSG_INFO, "WEP not suppported in WPS"); + wpa_printf(MSG_DEBUG, "WEP not suppported in WPS"); return false; } @@ -916,7 +875,7 @@ int wps_sm_rx_eapol(u8 *src_addr, u8 *buf, u32 len) } os_memcpy(param->buf, buf, len); param->len = len; - os_memcpy(param->sa, src_addr, WPS_ADDR_LEN); + os_memcpy(param->sa, src_addr, ETH_ALEN); wps_rxq_enqueue(param); return wps_post(SIG_WPS_RX, 0); @@ -1041,7 +1000,7 @@ int wps_sm_rx_eapol_internal(u8 *src_addr, u8 *buf, u32 len) tmp = (u8 *)(ehdr + 1) + 1; ret = wps_process_wps_mX_req(tmp, plen - sizeof(*ehdr) - 1, &res); - if (ret == 0 && res != WPS_FAILURE && res != WPS_IGNORE && res != WPS_FRAGMENT) { + if (ret == 0 && res != WPS_FAILURE && res != WPS_FRAGMENT) { ret = wps_send_wps_mX_rsp(ehdr->identifier); if (ret == 0) { wpa_printf(MSG_DEBUG, "sm->wps->state = %d", sm->wps->state); @@ -1050,9 +1009,6 @@ int wps_sm_rx_eapol_internal(u8 *src_addr, u8 *buf, u32 len) } else if (ret == 0 && res == WPS_FRAGMENT) { wpa_printf(MSG_DEBUG, "wps frag, continue..."); ret = ESP_OK; - } else if (res == WPS_IGNORE) { - wpa_printf(MSG_DEBUG, "IGNORE overlap Mx"); - ret = ESP_OK; /* IGNORE the overlap */ } else { ret = ESP_FAIL; } @@ -1382,12 +1338,32 @@ static int save_credentials_cb(void *ctx, const struct wps_credential *cred) return ESP_OK; } +int wps_init_cfg_pin(struct wps_config *cfg) +{ + if (wps_get_type() != WPS_TYPE_PIN) { + cfg->pbc = 1; + return 0; + } + + cfg->pbc = 0; + if (os_strncmp((char *)cfg->pin, "00000000", 8) != 0) { + unsigned int spin = 0; + cfg->dev_pw_id = DEV_PW_DEFAULT; + cfg->pin_len = 8; + if (wps_generate_pin(&spin) < 0) { + return -1; + } + os_sprintf((char *)cfg->pin, "%08d", spin); + } + + return 0; +} + int wifi_station_wps_init(void) { struct wps_funcs *wps_cb; struct wps_sm *sm = NULL; - uint8_t mac[6]; struct wps_config cfg = {0}; if (gWpsSm) { @@ -1403,8 +1379,7 @@ wifi_station_wps_init(void) sm = gWpsSm; - esp_wifi_get_macaddr_internal(WIFI_IF_STA, mac); - os_memcpy(sm->ownaddr, mac, ETH_ALEN); + esp_wifi_get_macaddr_internal(WIFI_IF_STA, sm->ownaddr); sm->identity_len = WSC_ID_ENROLLEE_LEN; os_memcpy(sm->identity, WSC_ID_ENROLLEE, sm->identity_len); @@ -1419,34 +1394,17 @@ wifi_station_wps_init(void) } cfg.wps = sm->wps_ctx; - if (IS_WPS_REGISTRAR(wps_get_type())) { - cfg.registrar = 1; - } - - /* TODO add for Registrar */ - if (wps_get_type() == WPS_TYPE_PIN) { - unsigned int spin = 0; - cfg.dev_pw_id = DEV_PW_DEFAULT; - cfg.pin_len = 8; - cfg.pin = os_zalloc(cfg.pin_len + 1); - if (!cfg.pin) { - goto _err; - } - if (wps_generate_pin(&spin) < 0) { - goto _err; - } - os_sprintf((char *)cfg.pin, "%08d", spin); - } else if (wps_get_type() == WPS_TYPE_PBC) { - cfg.pbc = 1; + + if (wps_init_cfg_pin(&cfg) < 0) { + goto _err; } + os_memcpy(cfg.wps->uuid, sm->uuid, WPS_UUID_LEN); if ((sm->wps = wps_init(&cfg)) == NULL) { /* alloc wps_data */ goto _err; } - if (cfg.pin) { - os_free((u8 *)cfg.pin); - } + /* Report PIN */ if (wps_get_type() == WPS_TYPE_PIN) { wifi_event_sta_wps_er_pin_t evt; os_memcpy(evt.pin_code, sm->wps->dev_password, 8); @@ -1478,13 +1436,12 @@ wifi_station_wps_init(void) wps_cb = os_malloc(sizeof(struct wps_funcs)); if (wps_cb == NULL) { goto _err; - } else { - wps_cb->wps_parse_scan_result = wps_parse_scan_result; - wps_cb->wifi_station_wps_start = wifi_station_wps_start; - wps_cb->wps_sm_rx_eapol = wps_sm_rx_eapol; - wps_cb->wps_start_pending = wps_start_pending; - esp_wifi_set_wps_cb_internal(wps_cb); } + wps_cb->wps_parse_scan_result = wps_parse_scan_result; + wps_cb->wifi_station_wps_start = wifi_station_wps_start; + wps_cb->wps_sm_rx_eapol = wps_sm_rx_eapol; + wps_cb->wps_start_pending = wps_start_pending; + esp_wifi_set_wps_cb_internal(wps_cb); return ESP_OK; @@ -1647,6 +1604,11 @@ void wifi_wps_scan(void) #endif } +static int wps_rf_band_cb(void *ctx) +{ + return WPS_RF_24GHZ; +} + int wifi_station_wps_start(void) { struct wps_sm *sm = wps_sm_get(); @@ -1666,7 +1628,7 @@ int wifi_station_wps_start(void) sm->wps->wps->dh_privkey = wpabuf_dup(sm->wps->dh_privkey); sm->wps->wps->dh_ctx = sm->wps->dh_ctx; sm->wps->wps->dh_pubkey = sm->wps->dh_pubkey_e; - sm->wps->wps->rf_band_cb = NULL; + sm->wps->wps->rf_band_cb = wps_rf_band_cb; wpabuf_clear_free(sm->wps->dh_privkey); sm->wps->dh_privkey = NULL; wifi_wps_scan(); @@ -1872,12 +1834,6 @@ int wifi_wps_enable_internal(const esp_wps_config_t *config) return ESP_ERR_WIFI_WPS_TYPE; } - /* currently , we don't support REGISTRAR */ - if (IS_WPS_REGISTRAR(config->wps_type)) { - wpa_printf(MSG_ERROR, "wps enable: not support registrar"); - return ESP_ERR_WIFI_WPS_TYPE; - } - wpa_printf(MSG_DEBUG, "Set factory information."); ret = wps_set_factory_info(config); if (ret != 0) { @@ -1974,13 +1930,11 @@ int esp_wifi_wps_start(int timeout_ms) } wpa_printf(MSG_DEBUG, "wps scan"); - #ifdef USE_WPS_TASK wps_post_block(SIG_WPS_START, 0); #else ic_pp_post(SIG_PP_WPS, 0); #endif - API_MUTEX_GIVE(); return ESP_OK; } diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wps_i.h b/components/wpa_supplicant/esp_supplicant/src/esp_wps_i.h index 945fd489b8..d397f00837 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wps_i.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wps_i.h @@ -4,6 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include "esp_wifi_driver.h" +#include "esp_wps.h" + /* WPS message flag */ enum wps_msg_flag { WPS_MSG_FLAG_MORE = 0x01, @@ -38,6 +41,10 @@ struct discard_ap_list_t{ #define MAX_PASSPHRASE_LEN 64 #endif +#ifndef MAX_CRED_COUNT +#define MAX_CRED_COUNT 10 +#endif + #define WPS_OUTBUF_SIZE 500 struct wps_sm { struct wps_config *wps_cfg; @@ -72,9 +79,47 @@ struct wps_sm { u8 discard_ap_cnt; }; +#define API_MUTEX_TAKE() do {\ + if (!s_wps_api_lock) {\ + s_wps_api_lock = xSemaphoreCreateRecursiveMutex();\ + if (!s_wps_api_lock) {\ + wpa_printf(MSG_ERROR, "wps api lock create failed");\ + return ESP_ERR_NO_MEM;\ + }\ + }\ + xSemaphoreTakeRecursive(s_wps_api_lock, portMAX_DELAY);\ +} while(0) + +#define API_MUTEX_GIVE() xSemaphoreGiveRecursive(s_wps_api_lock) +#define DATA_MUTEX_TAKE() xSemaphoreTakeRecursive(s_wps_data_lock, portMAX_DELAY) +#define DATA_MUTEX_GIVE() xSemaphoreGiveRecursive(s_wps_data_lock) + struct wps_sm *wps_sm_get(void); int wps_station_wps_unregister_cb(void); int wps_start_pending(void); int wps_sm_rx_eapol(u8 *src_addr, u8 *buf, u32 len); int wps_dev_deinit(struct wps_device_data *dev); +int wps_dev_init(void); +int wps_set_factory_info(const esp_wps_config_t *config); + +static inline int wps_get_type(void) +{ + return esp_wifi_get_wps_type_internal(); +} + +static inline int wps_set_type(uint32_t type) +{ + return esp_wifi_set_wps_type_internal(type); +} + +static inline int wps_get_status(void) +{ + return esp_wifi_get_wps_status_internal(); +} + +static inline int wps_set_status(uint32_t status) +{ + return esp_wifi_set_wps_status_internal(status); +} +int wps_init_cfg_pin(struct wps_config *cfg); diff --git a/components/wpa_supplicant/include/utils/wpa_debug.h b/components/wpa_supplicant/include/utils/wpa_debug.h index 4664975e15..02f1fb4f4e 100644 --- a/components/wpa_supplicant/include/utils/wpa_debug.h +++ b/components/wpa_supplicant/include/utils/wpa_debug.h @@ -65,14 +65,6 @@ void wpa_debug_print_timestamp(void); #define wpa_dbg(ctx, level, fmt, args...) wpa_printf(level, fmt, ##args) void wpa_dump_mem(char* desc, uint8_t *addr, uint16_t len); -static inline void wpa_hexdump_ascii(int level, const char *title, const void *buf, size_t len) -{ - -} - -static inline void wpa_hexdump_ascii_key(int level, const char *title, const void *buf, size_t len) -{ -} /** * wpa_hexdump - conditional hex dump @@ -87,6 +79,16 @@ static inline void wpa_hexdump_ascii_key(int level, const char *title, const voi */ void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len); +static inline void wpa_hexdump_ascii(int level, const char *title, const void *buf, size_t len) +{ + wpa_hexdump(level, title, buf, len); +} + +static inline void wpa_hexdump_ascii_key(int level, const char *title, const void *buf, size_t len) +{ + wpa_hexdump(level, title, buf, len); +} + static inline void wpa_hexdump_buf(int level, const char *title, const struct wpabuf *buf) { @@ -108,7 +110,6 @@ static inline void wpa_hexdump_buf(int level, const char *title, */ void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len); - static inline void wpa_hexdump_buf_key(int level, const char *title, const struct wpabuf *buf) { diff --git a/components/wpa_supplicant/src/ap/ap_config.h b/components/wpa_supplicant/src/ap/ap_config.h index 71d9be9091..62b4c423aa 100644 --- a/components/wpa_supplicant/src/ap/ap_config.h +++ b/components/wpa_supplicant/src/ap/ap_config.h @@ -12,7 +12,7 @@ #include "common/defs.h" #include "common/wpa_common.h" -#define MAX_STA_COUNT 4 +#define MAX_STA_COUNT 10 #define MAX_VLAN_ID 4094 typedef u8 macaddr[ETH_ALEN]; @@ -105,7 +105,6 @@ struct hostapd_wpa_psk { u8 addr[ETH_ALEN]; }; -#if 0 struct hostapd_eap_user { struct hostapd_eap_user *next; u8 *identity; @@ -124,6 +123,7 @@ struct hostapd_eap_user { int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ }; +#if 0 struct hostapd_radius_attr { u8 type; struct wpabuf *val; @@ -247,7 +247,11 @@ struct hostapd_bss_config { */ u16 max_listen_interval; + int wps_state; #ifdef CONFIG_WPS +#define WPS_DEV_TYPE_LEN 8 +/* maximum number of advertised WPS vendor extension attributes */ +#define MAX_WPS_VENDOR_EXTENSIONS 10 int ap_setup_locked; u8 uuid[16]; char *wps_pin_requests; diff --git a/components/wpa_supplicant/src/ap/eap_user_db.c b/components/wpa_supplicant/src/ap/eap_user_db.c new file mode 100644 index 0000000000..41fb245d1f --- /dev/null +++ b/components/wpa_supplicant/src/ap/eap_user_db.c @@ -0,0 +1,268 @@ +/* + * hostapd / EAP user database + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ + +#include "common.h" +#include "eap_common/eap_wsc_common.h" +#include "eap_server/eap_methods.h" +#include "eap_server/eap.h" +#include "ap_config.h" +#include "hostapd.h" + +#ifdef CONFIG_SQLITE + +static void set_user_methods(struct hostapd_eap_user *user, const char *methods) +{ + char *buf, *start; + int num_methods; + + buf = os_strdup(methods); + if (buf == NULL) + return; + + os_memset(&user->methods, 0, sizeof(user->methods)); + num_methods = 0; + start = buf; + while (*start) { + char *pos3 = os_strchr(start, ','); + if (pos3) + *pos3++ = '\0'; + user->methods[num_methods].method = + eap_server_get_type(start, + &user->methods[num_methods].vendor); + if (user->methods[num_methods].vendor == EAP_VENDOR_IETF && + user->methods[num_methods].method == EAP_TYPE_NONE) { + if (os_strcmp(start, "TTLS-PAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_PAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-CHAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_CHAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-MSCHAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_MSCHAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2; + goto skip_eap; + } + wpa_printf(MSG_INFO, "DB: Unsupported EAP type '%s'", + start); + os_free(buf); + return; + } + + num_methods++; + if (num_methods >= EAP_MAX_METHODS) + break; + skip_eap: + if (pos3 == NULL) + break; + start = pos3; + } + + os_free(buf); +} + + +static int get_user_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct hostapd_eap_user *user = ctx; + int i; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "password") == 0 && argv[i]) { + bin_clear_free(user->password, user->password_len); + user->password_len = os_strlen(argv[i]); + user->password = (u8 *) os_strdup(argv[i]); + user->next = (void *) 1; + } else if (os_strcmp(col[i], "methods") == 0 && argv[i]) { + set_user_methods(user, argv[i]); + } else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) { + user->remediation = strlen(argv[i]) > 0; + } else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) { + user->t_c_timestamp = strtol(argv[i], NULL, 10); + } + } + + return 0; +} + + +static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct hostapd_eap_user *user = ctx; + int i, id = -1, methods = -1; + size_t len; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "identity") == 0 && argv[i]) + id = i; + else if (os_strcmp(col[i], "methods") == 0 && argv[i]) + methods = i; + } + + if (id < 0 || methods < 0) + return 0; + + len = os_strlen(argv[id]); + if (len <= user->identity_len && + os_memcmp(argv[id], user->identity, len) == 0 && + (user->password == NULL || len > user->password_len)) { + bin_clear_free(user->password, user->password_len); + user->password_len = os_strlen(argv[id]); + user->password = (u8 *) os_strdup(argv[id]); + user->next = (void *) 1; + set_user_methods(user, argv[methods]); + } + + return 0; +} + + +static const struct hostapd_eap_user * +eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2) +{ + sqlite3 *db; + struct hostapd_eap_user *user = NULL; + char id_str[256], cmd[300]; + size_t i; + int res; + + if (identity_len >= sizeof(id_str)) { + wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d", + __func__, (int) identity_len, + (int) (sizeof(id_str))); + return NULL; + } + os_memcpy(id_str, identity, identity_len); + id_str[identity_len] = '\0'; + for (i = 0; i < identity_len; i++) { + if (id_str[i] >= 'a' && id_str[i] <= 'z') + continue; + if (id_str[i] >= 'A' && id_str[i] <= 'Z') + continue; + if (id_str[i] >= '0' && id_str[i] <= '9') + continue; + if (id_str[i] == '-' || id_str[i] == '_' || id_str[i] == '.' || + id_str[i] == ',' || id_str[i] == '@' || id_str[i] == '\\' || + id_str[i] == '!' || id_str[i] == '#' || id_str[i] == '%' || + id_str[i] == '=' || id_str[i] == ' ') + continue; + wpa_printf(MSG_INFO, "DB: Unsupported character in identity"); + return NULL; + } + + bin_clear_free(hapd->tmp_eap_user.identity, + hapd->tmp_eap_user.identity_len); + bin_clear_free(hapd->tmp_eap_user.password, + hapd->tmp_eap_user.password_len); + os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user)); + hapd->tmp_eap_user.phase2 = phase2; + hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1); + if (hapd->tmp_eap_user.identity == NULL) + return NULL; + os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len); + hapd->tmp_eap_user.identity_len = identity_len; + + if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) { + wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s", + hapd->conf->eap_user_sqlite, sqlite3_errmsg(db)); + sqlite3_close(db); + return NULL; + } + + res = os_snprintf(cmd, sizeof(cmd), + "SELECT * FROM users WHERE identity='%s' AND phase2=%d;", + id_str, phase2); + if (os_snprintf_error(sizeof(cmd), res)) + goto fail; + + wpa_printf(MSG_DEBUG, "DB: %s", cmd); + if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) != + SQLITE_OK) { + wpa_printf(MSG_DEBUG, + "DB: Failed to complete SQL operation: %s db: %s", + sqlite3_errmsg(db), hapd->conf->eap_user_sqlite); + } else if (hapd->tmp_eap_user.next) + user = &hapd->tmp_eap_user; + + if (user == NULL && !phase2) { + os_snprintf(cmd, sizeof(cmd), + "SELECT identity,methods FROM wildcards;"); + wpa_printf(MSG_DEBUG, "DB: %s", cmd); + if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user, + NULL) != SQLITE_OK) { + wpa_printf(MSG_DEBUG, + "DB: Failed to complete SQL operation: %s db: %s", + sqlite3_errmsg(db), + hapd->conf->eap_user_sqlite); + } else if (hapd->tmp_eap_user.next) { + user = &hapd->tmp_eap_user; + os_free(user->identity); + user->identity = user->password; + user->identity_len = user->password_len; + user->password = NULL; + user->password_len = 0; + } + } + +fail: + sqlite3_close(db); + + return user; +} + +#endif /* CONFIG_SQLITE */ + + +const struct hostapd_eap_user * +hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2) +{ + const struct hostapd_bss_config *conf = hapd->conf; + +#ifdef CONFIG_WPS + if (identity_len == WSC_ID_ENROLLEE_LEN && + os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) { + static struct hostapd_eap_user wsc_enrollee; + os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee)); + wsc_enrollee.methods[0].method = eap_server_get_type( + "WSC", &wsc_enrollee.methods[0].vendor); + return &wsc_enrollee; + } + + if (identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) { + static struct hostapd_eap_user wsc_registrar; + os_memset(&wsc_registrar, 0, sizeof(wsc_registrar)); + wsc_registrar.methods[0].method = eap_server_get_type( + "WSC", &wsc_registrar.methods[0].vendor); + wsc_registrar.password = (u8 *) conf->ap_pin; + wsc_registrar.password_len = conf->ap_pin ? + os_strlen(conf->ap_pin) : 0; + return &wsc_registrar; + } +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_SQLITE + if (user == NULL && conf->eap_user_sqlite) { + return eap_user_sqlite_get(hapd, identity, identity_len, + phase2); + } +#endif /* CONFIG_SQLITE */ + + return NULL; +} diff --git a/components/wpa_supplicant/src/ap/hostapd.h b/components/wpa_supplicant/src/ap/hostapd.h index 5bedf09d0a..92192c609f 100644 --- a/components/wpa_supplicant/src/ap/hostapd.h +++ b/components/wpa_supplicant/src/ap/hostapd.h @@ -63,6 +63,25 @@ struct hostapd_frame_info { int ssi_signal; /* dBm */ }; +#ifdef CONFIG_WPS +enum hapd_wps_status { + WPS_SUCCESS_STATUS = 1, + WPS_FAILURE_STATUS +}; + +enum pbc_status { + WPS_PBC_STATUS_DISABLE, + WPS_PBC_STATUS_ACTIVE, + WPS_PBC_STATUS_TIMEOUT, + WPS_PBC_STATUS_OVERLAP +}; + +struct wps_stat { + enum hapd_wps_status status; + enum pbc_status pbc_status; + u8 peer_addr[ETH_ALEN]; +}; +#endif /** * struct hostapd_data - hostapd per-BSS data structure @@ -73,9 +92,13 @@ struct hostapd_data { int interface_added; /* virtual interface added for this BSS */ u8 own_addr[ETH_ALEN]; - + struct sta_info *sta_list; /* STA info list head */ +#define STA_HASH_SIZE 10 +#define STA_HASH(sta) (sta[5] & 0xa) + struct sta_info *sta_hash[STA_HASH_SIZE]; int num_sta; /* number of entries in sta_list */ + struct eapol_authenticator *eapol_auth; struct wpa_authenticator *wpa_auth; #ifdef CONFIG_FULL_DYNAMIC_VLAN @@ -83,10 +106,15 @@ struct hostapd_data { #endif /* CONFIG_FULL_DYNAMIC_VLAN */ #ifdef CONFIG_WPS + struct wps_context *wps; unsigned int ap_pin_failures; unsigned int ap_pin_failures_consecutive; struct upnp_wps_device_sm *wps_upnp; unsigned int ap_pin_lockout_time; + + struct wps_stat wps_stats; + void (*wps_event_cb)(void *ctx, enum wps_event event, + union wps_event_data *data); #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P @@ -112,4 +140,10 @@ struct hostapd_data { #endif /* CONFIG_SQLITE */ }; +struct hostapd_data *hostapd_get_hapd_data(void); + +const struct hostapd_eap_user * +hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2); + #endif /* HOSTAPD_H */ diff --git a/components/wpa_supplicant/src/ap/ieee802_1x.c b/components/wpa_supplicant/src/ap/ieee802_1x.c index 9f8fd1bd8e..5e3c078baf 100644 --- a/components/wpa_supplicant/src/ap/ieee802_1x.c +++ b/components/wpa_supplicant/src/ap/ieee802_1x.c @@ -9,14 +9,138 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" #include "crypto/crypto.h" #include "common/ieee802_11_defs.h" #include "hostapd.h" #include "ap/sta_info.h" #include "ap/wpa_auth.h" +#include "eap_server/eap.h" #include "ap/ap_config.h" +#include "eap_common/eap_wsc_common.h" #include "ap/ieee802_1x.h" #include "utils/wpa_debug.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "eapol_auth/eapol_auth_sm_i.h" +#include "eap_server/eap.h" +#include "sta_info.h" +#include "ieee802_1x.h" + +int hostapd_send_eapol(const u8 *source, const u8 *sta_addr, + const u8 *data, size_t data_len); + +static void ieee802_1x_finished(struct hostapd_data *hapd, + struct sta_info *sta, int success, + int remediation); + +static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 type, const u8 *data, size_t datalen) +{ + u8 *buf; + struct ieee802_1x_hdr *xhdr; + size_t len; + + len = sizeof(*xhdr) + datalen; + buf = os_zalloc(len); + if (!buf) { + wpa_printf(MSG_ERROR, "malloc() failed for %s(len=%lu)", + __func__, (unsigned long) len); + return; + } + + xhdr = (struct ieee802_1x_hdr *) buf; + xhdr->version = EAPOL_VERSION; + xhdr->type = type; + xhdr->length = host_to_be16(datalen); + + if (datalen > 0 && data != NULL) + os_memcpy(xhdr + 1, data, datalen); + + hostapd_send_eapol(hapd->own_addr, sta->addr, buf, len); + os_free(buf); +} + + + +static void handle_eap_response(struct hostapd_data *hapd, + struct sta_info *sta, struct eap_hdr *eap, + size_t len) +{ + u8 type, *data; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (!sm) + return; + + data = (u8 *) (eap + 1); + + if (len < sizeof(*eap) + 1) { + wpa_printf(MSG_INFO, "%s: too short response data", __func__); + return; + } + + sm->eap_type_supp = type = data[0]; + + sm->dot1xAuthEapolRespFramesRx++; + + wpabuf_free(sm->eap_if->eapRespData); + sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len); + sm->eapolEap = true; +} + +/* Process incoming EAP packet from Supplicant */ +static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len) +{ + struct eap_hdr *eap; + u16 eap_len; + + if (len < sizeof(*eap)) { + wpa_printf(MSG_INFO, " too short EAP packet"); + return; + } + + eap = (struct eap_hdr *) buf; + + eap_len = be_to_host16(eap->length); + wpa_printf(MSG_DEBUG, "EAP: code=%d identifier=%d length=%d", + eap->code, eap->identifier, + eap_len); + if (eap_len < sizeof(*eap)) { + wpa_printf(MSG_DEBUG, " Invalid EAP length"); + return; + } else if (eap_len > len) { + wpa_printf(MSG_DEBUG, + " Too short frame to contain this EAP packet"); + return; + } else if (eap_len < len) { + wpa_printf(MSG_DEBUG, + " Ignoring %lu extra bytes after EAP packet", + (unsigned long) len - eap_len); + } + + switch (eap->code) { + case EAP_CODE_RESPONSE: + handle_eap_response(hapd, sta, eap, eap_len); + break; + case EAP_CODE_INITIATE: + break; + } +} + +struct eapol_state_machine * +ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta) +{ + int flags = 0; + + if (sta->wpa_sm) { + flags |= EAPOL_SM_USES_WPA; + } + return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags, + sta->wps_ie, NULL, sta, + sta->identity, NULL); +} + /** * ieee802_1x_receive - Process the EAPOL frames from the Supplicant @@ -35,35 +159,44 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, struct ieee802_1x_eapol_key *key; u16 datalen; - wpa_printf( MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR, + wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR, (unsigned long) len, MAC2STR(sa)); sta = ap_get_sta(hapd, sa); - if (!sta || !(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH))) { - wpa_printf( MSG_DEBUG, "IEEE 802.1X data frame from not " - "associated/Pre-authenticating STA"); + if (!sta) { + wpa_printf(MSG_DEBUG, + "IEEE 802.1X data frame from not associated/Pre-authenticating STA"); + return; } if (len < sizeof(*hdr)) { - wpa_printf( MSG_DEBUG, " too short IEEE 802.1X packet\n"); + wpa_printf(MSG_INFO, " too short IEEE 802.1X packet"); return; } hdr = (struct ieee802_1x_hdr *) buf; datalen = be_to_host16(hdr->length); - wpa_printf( MSG_DEBUG, " IEEE 802.1X: version=%d type=%d length=%d", + wpa_printf(MSG_DEBUG, " IEEE 802.1X: version=%d type=%d length=%d", hdr->version, hdr->type, datalen); if (len - sizeof(*hdr) < datalen) { - wpa_printf( MSG_DEBUG, " frame too short for this IEEE 802.1X packet\n"); + wpa_printf(MSG_INFO, + " frame too short for this IEEE 802.1X packet"); + if (sta->eapol_sm) + sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++; return; } if (len - sizeof(*hdr) > datalen) { - wpa_printf( MSG_DEBUG, " ignoring %lu extra octets after " - "IEEE 802.1X packet", + wpa_printf(MSG_DEBUG, + " ignoring %lu extra octets after IEEE 802.1X packet", (unsigned long) len - sizeof(*hdr) - datalen); } + if (sta->eapol_sm) { + sta->eapol_sm->dot1xAuthLastEapolFrameVersion = hdr->version; + sta->eapol_sm->dot1xAuthEapolFramesRx++; + } + key = (struct ieee802_1x_eapol_key *) (hdr + 1); if (datalen >= sizeof(struct ieee802_1x_eapol_key) && hdr->type == IEEE802_1X_TYPE_EAPOL_KEY && @@ -73,4 +206,274 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, sizeof(*hdr) + datalen); return; } + + if (!sta->eapol_sm) { + sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta); + if (!sta->eapol_sm) + return; + +#ifdef CONFIG_WPS + if (!hapd->conf->ieee802_1x && hapd->conf->wps_state) { + u32 wflags = sta->flags & (WLAN_STA_WPS | + WLAN_STA_WPS2 | + WLAN_STA_MAYBE_WPS); + if (wflags == WLAN_STA_MAYBE_WPS || + wflags == (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) { + /* + * Delay EAPOL frame transmission until a + * possible WPS STA initiates the handshake + * with EAPOL-Start. Only allow the wait to be + * skipped if the STA is known to support WPS + * 2.0. + */ + wpa_printf(MSG_DEBUG, + "WPS: Do not start EAPOL until EAPOL-Start is received"); + sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; + } + } +#endif /* CONFIG_WPS */ + + sta->eapol_sm->eap_if->portEnabled = true; + } + + /* since we support version 1, we can ignore version field and proceed + * as specified in version 1 standard [IEEE Std 802.1X-2001, 7.5.5] */ + /* TODO: actually, we are not version 1 anymore.. However, Version 2 + * does not change frame contents, so should be ok to process frames + * more or less identically. Some changes might be needed for + * verification of fields. */ + + switch (hdr->type) { + case IEEE802_1X_TYPE_EAP_PACKET: + handle_eap(hapd, sta, (u8 *) (hdr + 1), datalen); + break; + + case IEEE802_1X_TYPE_EAPOL_START: + sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; + sta->eapol_sm->eapolStart = true; + sta->eapol_sm->dot1xAuthEapolStartFramesRx++; + eap_server_clear_identity(sta->eapol_sm->eap); + wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL); + break; + + + case IEEE802_1X_TYPE_EAPOL_LOGOFF: + break; + + case IEEE802_1X_TYPE_EAPOL_KEY: + wpa_printf(MSG_DEBUG, " EAPOL-Key"); + break; + + case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT: + wpa_printf(MSG_DEBUG, " EAPOL-Encapsulated-ASF-Alert"); + /* TODO: implement support for this; show data */ + break; + + default: + wpa_printf(MSG_DEBUG, " unknown IEEE 802.1X packet type"); + sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++; + break; + } + + eapol_auth_step(sta->eapol_sm); +} + + +void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct eapol_state_machine *sm = sta->eapol_sm; + + if (!sm) + return; + + sta->eapol_sm = NULL; + eapol_auth_free(sm); +} + + +static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type, + const u8 *data, size_t datalen) +{ +#ifdef CONFIG_WPS + struct sta_info *sta = sta_ctx; + + if ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) == + WLAN_STA_MAYBE_WPS) { + const u8 *identity; + size_t identity_len; + struct eapol_state_machine *sm = sta->eapol_sm; + + identity = eap_get_identity(sm->eap, &identity_len); + if (identity && + ((identity_len == WSC_ID_ENROLLEE_LEN && + os_memcmp(identity, WSC_ID_ENROLLEE, + WSC_ID_ENROLLEE_LEN) == 0) || + (identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(identity, WSC_ID_REGISTRAR, + WSC_ID_REGISTRAR_LEN) == 0))) { + wpa_printf(MSG_DEBUG, + "WPS: WLAN_STA_MAYBE_WPS -> WLAN_STA_WPS"); + sta->flags |= WLAN_STA_WPS; + } + } +#endif /* CONFIG_WPS */ + + ieee802_1x_send(ctx, sta_ctx, type, data, datalen); +} + + +static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx, + const u8 *data, size_t datalen) +{ +#ifndef CONFIG_NO_RADIUS + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + + ieee802_1x_encapsulate_radius(hapd, sta, data, datalen); +#endif /* CONFIG_NO_RADIUS */ +} + + +static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success, + int preauth, int remediation) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + ieee802_1x_finished(hapd, sta, success, remediation); +} + + +static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) +{ + struct hostapd_data *hapd = ctx; + const struct hostapd_eap_user *eap_user; + int i; + int rv = -1; + + eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2); + if (!eap_user) + goto out; + + os_memset(user, 0, sizeof(*user)); + user->phase2 = phase2; + for (i = 0; i < EAP_MAX_METHODS; i++) { + user->methods[i].vendor = eap_user->methods[i].vendor; + user->methods[i].method = eap_user->methods[i].method; + } + + if (eap_user->password) { + user->password = os_memdup(eap_user->password, + eap_user->password_len); + if (!user->password) + goto out; + user->password_len = eap_user->password_len; + user->password_hash = eap_user->password_hash; + } + user->force_version = eap_user->force_version; + user->ttls_auth = eap_user->ttls_auth; + rv = 0; + +out: + if (rv) + wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__); + + return rv; +} + + +static int ieee802_1x_sta_entry_alive(void *ctx, const u8 *addr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->eapol_sm) + return 0; + return 1; +} + + +static void ieee802_1x_set_port_authorized(void *ctx, void *sta_ctx, + int authorized) +{ +} + + +static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx) +{ +} + + +static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx, + enum eapol_event type) +{ +#if 0 + /* struct hostapd_data *hapd = ctx; */ + struct sta_info *sta = sta_ctx; + + switch (type) { + case EAPOL_AUTH_SM_CHANGE: + wpa_auth_sm_notify(sta->wpa_sm); + break; + case EAPOL_AUTH_REAUTHENTICATE: + wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL); + break; + } +#endif +} + + +int ieee802_1x_init(struct hostapd_data *hapd) +{ + struct eapol_auth_config conf; + struct eapol_auth_cb cb; + struct eap_config *eap_cfg; + + os_memset(&conf, 0, sizeof(conf)); + eap_cfg = os_zalloc(sizeof(struct eap_config)); + eap_cfg->max_auth_rounds = 100; + eap_cfg->max_auth_rounds_short = 50; + //eap_cfg->backend_auth = 1; + eap_cfg->eap_server = 1; + conf.eap_cfg = eap_cfg; + conf.ctx = hapd; + conf.wpa = hapd->conf->wpa; + + os_memset(&cb, 0, sizeof(cb)); + cb.eapol_send = ieee802_1x_eapol_send; + cb.aaa_send = ieee802_1x_aaa_send; + cb.finished = _ieee802_1x_finished; + cb.get_eap_user = ieee802_1x_get_eap_user; + cb.sta_entry_alive = ieee802_1x_sta_entry_alive; + cb.set_port_authorized = ieee802_1x_set_port_authorized; + cb.abort_auth = _ieee802_1x_abort_auth; + cb.eapol_event = ieee802_1x_eapol_event; + + hapd->eapol_auth = eapol_auth_init(&conf, &cb); + if (!hapd->eapol_auth) + return -1; + + return 0; +} + + +static void ieee802_1x_finished(struct hostapd_data *hapd, + struct sta_info *sta, int success, + int remediation) +{ + if (!success) { + /* + * Many devices require deauthentication after WPS provisioning + * and some may not be be able to do that themselves, so + * disconnect the client here. In addition, this may also + * benefit IEEE 802.1X/EAPOL authentication cases, too since + * the EAPOL PAE state machine would remain in HELD state for + * considerable amount of time and some EAP methods, like + * EAP-FAST with anonymous provisioning, may require another + * EAPOL authentication to be started to complete connection. + */ + ap_sta_delayed_1x_auth_fail_disconnect(hapd, sta); + } } diff --git a/components/wpa_supplicant/src/ap/ieee802_1x.h b/components/wpa_supplicant/src/ap/ieee802_1x.h index 60051ee5a0..7eb4f14c1c 100644 --- a/components/wpa_supplicant/src/ap/ieee802_1x.h +++ b/components/wpa_supplicant/src/ap/ieee802_1x.h @@ -17,8 +17,12 @@ struct hostapd_bss_config; struct hostapd_radius_attr; struct radius_msg; - void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, size_t len); +void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta); +int ieee802_1x_init(struct hostapd_data *hapd); +struct eapol_state_machine * +ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta); + #endif /* IEEE802_1X_H */ diff --git a/components/wpa_supplicant/src/ap/sta_info.c b/components/wpa_supplicant/src/ap/sta_info.c new file mode 100644 index 0000000000..77b5449144 --- /dev/null +++ b/components/wpa_supplicant/src/ap/sta_info.c @@ -0,0 +1,208 @@ +/* + * hostapd / Station table + * Copyright (c) 2002-2017, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "crypto/crypto.h" +#include "hostapd.h" +#include "ieee802_1x.h" +#include "wpa_auth.h" +#include "ap_config.h" +#include "sta_info.h" + +static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx); +void hostapd_wps_eap_completed(struct hostapd_data *hapd); + +int ap_for_each_sta(struct hostapd_data *hapd, + int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, + void *ctx), + void *ctx) +{ + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (cb(hapd, sta, ctx)) + return 1; + } + + return 0; +} + + +struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) +{ + struct sta_info *s; + + s = hapd->sta_hash[STA_HASH(sta)]; + while (s != NULL && os_memcmp(s->addr, sta, 6) != 0) + s = s->hnext; + return s; +} + + +static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct sta_info *tmp; + + if (hapd->sta_list == sta) { + hapd->sta_list = sta->next; + return; + } + + tmp = hapd->sta_list; + while (tmp != NULL && tmp->next != sta) + tmp = tmp->next; + if (tmp == NULL) { + wpa_printf(MSG_DEBUG, "Could not remove STA " MACSTR " from " + "list.", MAC2STR(sta->addr)); + } else + tmp->next = sta->next; +} + + +void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta) +{ + sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)]; + hapd->sta_hash[STA_HASH(sta->addr)] = sta; +} + + +static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct sta_info *s; + + s = hapd->sta_hash[STA_HASH(sta->addr)]; + if (s == NULL) return; + if (os_memcmp(s->addr, sta->addr, 6) == 0) { + hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext; + return; + } + + while (s->hnext != NULL && + os_memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0) + s = s->hnext; + if (s->hnext != NULL) + s->hnext = s->hnext->hnext; + else + wpa_printf(MSG_DEBUG, "AP: could not remove STA " MACSTR + " from hash table", MAC2STR(sta->addr)); +} + +void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) +{ + ap_sta_hash_del(hapd, sta); + ap_sta_list_del(hapd, sta); + + hapd->num_sta--; + + wpa_auth_sta_deinit(sta->wpa_sm); +#ifdef CONFIG_WPS_REGISTRAR + if (ap_sta_pending_delayed_1x_auth_fail_disconnect(hapd, sta)) + eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta); + + ieee802_1x_free_station(hapd, sta); + + wpabuf_free(sta->wps_ie); +#endif + + os_free(sta); +} + + +void hostapd_free_stas(struct hostapd_data *hapd) +{ + struct sta_info *sta, *prev; + + sta = hapd->sta_list; + + while (sta) { + prev = sta; + sta = sta->next; + wpa_printf(MSG_DEBUG, "Removing station " MACSTR, + MAC2STR(prev->addr)); + ap_free_sta(hapd, prev); + } +} + + +struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (sta) + return sta; + + wpa_printf(MSG_DEBUG, " New STA"); + if (hapd->num_sta >= hapd->conf->max_num_sta) { + /* FIX: might try to remove some old STAs first? */ + wpa_printf(MSG_DEBUG, "no more room for new STAs (%d/%d)", + hapd->num_sta, hapd->conf->max_num_sta); + return NULL; + } + + sta = os_zalloc(sizeof(struct sta_info)); + if (sta == NULL) { + wpa_printf(MSG_ERROR, "malloc failed"); + return NULL; + } + + /* initialize STA info data */ + os_memcpy(sta->addr, addr, ETH_ALEN); + sta->next = hapd->sta_list; + hapd->sta_list = sta; + hapd->num_sta++; + ap_sta_hash_add(hapd, sta); + + return sta; +} + +static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + u16 reason; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "IEEE 802.1X: Scheduled disconnection of " MACSTR + " after EAP-Failure", MAC2STR(sta->addr)); + + reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED; + esp_wifi_ap_deauth_internal(sta->addr, reason); + if (sta->flags & WLAN_STA_WPS) + hostapd_wps_eap_completed(hapd); +} + + +void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta) +{ + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "IEEE 802.1X: Force disconnection of " MACSTR + " after EAP-Failure in 10 ms", MAC2STR(sta->addr)); + + /* + * Add a small sleep to increase likelihood of previously requested + * EAP-Failure TX getting out before this should the driver reorder + * operations. + */ + eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta); + eloop_register_timeout(0, 10000, ap_sta_delayed_1x_auth_fail_cb, + hapd, sta); +} + + +int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta) +{ + return eloop_is_timeout_registered(ap_sta_delayed_1x_auth_fail_cb, + hapd, sta); +} diff --git a/components/wpa_supplicant/src/ap/sta_info.h b/components/wpa_supplicant/src/ap/sta_info.h index b187933f7e..f5ff61889f 100644 --- a/components/wpa_supplicant/src/ap/sta_info.h +++ b/components/wpa_supplicant/src/ap/sta_info.h @@ -1,6 +1,6 @@ /* * hostapd / Station table - * Copyright (c) 2002-2011, Jouni Malinen + * Copyright (c) 2002-2017, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -42,36 +42,20 @@ struct sta_info { struct sta_info *next; /* next entry in sta list */ struct sta_info *hnext; /* next entry in hash table list */ u8 addr[6]; - u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ u32 flags; /* Bitfield of WLAN_STA_* */ - u16 capability; - u16 listen_interval; /* or beacon_int for APs */ - u8 supported_rates[WLAN_SUPP_RATES_MAX]; - int supported_rates_len; - - u16 auth_alg; - - enum { - STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE - } timeout_next; + /* IEEE 802.1X related data */ + struct eapol_state_machine *eapol_sm; struct wpa_state_machine *wpa_sm; -#ifdef CONFIG_IEEE80211W - int sa_query_count; /* number of pending SA Query requests; - * 0 = no SA Query in progress */ - int sa_query_timed_out; - u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN * - * sa_query_count octets of pending SA Query - * transaction identifiers */ - struct os_time sa_query_start; -#endif /* CONFIG_IEEE80211W */ + char *identity; /* User-Name from RADIUS */ #ifdef CONFIG_INTERWORKING #define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */ struct gas_dialog_info *gas_dialog; u8 gas_dialog_next; #endif /* CONFIG_INTERWORKING */ + struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */ #ifdef CONFIG_SAE enum { SAE_INIT, SAE_COMMIT, SAE_CONFIRM } sae_state; @@ -137,5 +121,16 @@ 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); +void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta); +int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta); +int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta); + +void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta); #endif /* STA_INFO_H */ diff --git a/components/wpa_supplicant/src/ap/wpa_auth.c b/components/wpa_supplicant/src/ap/wpa_auth.c index 78269bb7ae..358662d832 100644 --- a/components/wpa_supplicant/src/ap/wpa_auth.c +++ b/components/wpa_supplicant/src/ap/wpa_auth.c @@ -18,6 +18,7 @@ #include "hostapd.h" #include "rsn_supp/wpa.h" #include "ap/ap_config.h" +#include "ap/sta_info.h" #include "common/wpa_common.h" #include "crypto/aes_wrap.h" @@ -29,6 +30,7 @@ #include "esp_wifi_driver.h" #include "esp_wifi.h" #include "esp_private/wifi.h" +#include "esp_wpas_glue.h" #define STATE_MACHINE_DATA struct wpa_state_machine #define STATE_MACHINE_DEBUG_PREFIX "WPA" @@ -183,22 +185,7 @@ static inline int wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *data, size_t data_len, int encrypt) { - void *buffer = os_malloc(data_len + sizeof(struct l2_ethhdr)); - struct l2_ethhdr *eth = buffer; - - if (!buffer){ - wpa_printf( MSG_DEBUG, "send_eapol, buffer=%p\n", buffer); - return -1; - } - - memcpy(eth->h_dest, addr, ETH_ALEN); - memcpy(eth->h_source, wpa_auth->addr, ETH_ALEN); - eth->h_proto = host_to_be16(ETH_P_EAPOL); - - memcpy((char *)buffer + sizeof(struct l2_ethhdr), data, data_len); - esp_wifi_internal_tx(1, buffer, sizeof(struct l2_ethhdr) + data_len); - os_free(buffer); - return 0; + return hostapd_send_eapol(wpa_auth->addr, addr, data, data_len); } int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, @@ -2406,11 +2393,12 @@ bool wpa_ap_join(void** sm, uint8_t *bssid, uint8_t *wpa_ie, uint8_t wpa_ie_len, bool wpa_ap_remove(void* sm) { - struct wpa_state_machine *wpa_sm; - if (!sm) return false; + struct hostapd_data *hapd = hostapd_get_hapd_data(); + if (!sm || !hapd) { + return false; + } - wpa_sm = (struct wpa_state_machine*)sm; - wpa_auth_sta_deinit(wpa_sm); + ap_free_sta(hapd, sm); return true; } diff --git a/components/wpa_supplicant/src/ap/wps_hostapd.c b/components/wpa_supplicant/src/ap/wps_hostapd.c new file mode 100644 index 0000000000..a03b91b8ac --- /dev/null +++ b/components/wpa_supplicant/src/ap/wps_hostapd.c @@ -0,0 +1,586 @@ +/* + * hostapd / WPS integration + * Copyright (c) 2008-2016, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/uuid.h" +#include "common/wpa_ctrl.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "wps/wps.h" +#include "wps/wps_defs.h" +#include "wps/wps_dev_attr.h" +#include "wps/wps_attr_parse.h" +#include "hostapd.h" +#include "ap_config.h" +#include "wpa_auth.h" +#include "wpa_auth_i.h" +#include "sta_info.h" +#include "wps/wps_i.h" +#include "wps_hostapd.h" +#include "eap_server/eap_methods.h" +#include "ieee802_1x.h" +#include "esp_wps_i.h" + +static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); + +static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie, + struct wpabuf *probe_resp_ie) +{ + int ret; + + wpa_printf(MSG_DEBUG, "WPS: Updating beacon IEs(%d) to driver", beacon_ie->used); + ret = esp_wifi_set_appie_internal(WIFI_APPIE_RAM_BEACON, + (uint8_t *)wpabuf_head(beacon_ie), beacon_ie->used, 0); + if (ret != ESP_OK) { + wpa_printf(MSG_ERROR, "WPS: Failed to update beacon IEs"); + goto cleanup; + } + wpa_printf(MSG_DEBUG, "WPS: Updating probe IEs(%d) to driver", probe_resp_ie->used); + ret = esp_wifi_set_appie_internal(WIFI_APPIE_RAM_PROBE_RSP, + (uint8_t *)wpabuf_head(probe_resp_ie), probe_resp_ie->used, 0); + +cleanup: + wpabuf_free(beacon_ie); + wpabuf_free(probe_resp_ie); + + return ret; +} + +struct wps_stop_reg_data { + struct hostapd_data *current_hapd; + const u8 *uuid_e; + const u8 *dev_pw; + size_t dev_pw_len; +}; + +static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx) +{ + struct wps_stop_reg_data *data = ctx; + if (hapd != data->current_hapd && hapd->wps != NULL) + wps_registrar_complete(hapd->wps->registrar, data->uuid_e, + data->dev_pw, data->dev_pw_len); + return 0; +} + +void hostapd_wps_eap_completed(struct hostapd_data *hapd) +{ +} + +static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr, + const u8 *uuid_e, const u8 *dev_pw, + size_t dev_pw_len) +{ + struct hostapd_data *hapd = ctx; + char uuid[40]; + struct wps_stop_reg_data data; + if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) + return; + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s", + MAC2STR(mac_addr), uuid); + data.current_hapd = hapd; + data.uuid_e = uuid_e; + data.dev_pw = dev_pw; + data.dev_pw_len = dev_pw_len; + wps_stop_registrar(hapd, &data); + wps_set_status(WPS_STATUS_DISABLE); + + /* TODO add callback event for freeRTOS */ +} + + +static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx) +{ + struct hostapd_data *hapd = eloop_data; + + if (hapd->conf->ap_setup_locked) + return; + if (hapd->ap_pin_failures_consecutive >= 10) + return; + + wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN"); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED); + hapd->wps->ap_setup_locked = 0; + wps_registrar_update_ie(hapd->wps->registrar); +} + + +static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx) +{ + struct wps_event_pwd_auth_fail *data = ctx; + + if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL) + return 0; + + /* + * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup + * for some time if this happens multiple times to slow down brute + * force attacks. + */ + hapd->ap_pin_failures++; + hapd->ap_pin_failures_consecutive++; + wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u " + "(%u consecutive)", + hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive); + if (hapd->ap_pin_failures < 3) + return 0; + + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED); + hapd->wps->ap_setup_locked = 1; + + wps_registrar_update_ie(hapd->wps->registrar); + + if (!hapd->conf->ap_setup_locked && + hapd->ap_pin_failures_consecutive >= 10) { + /* + * In indefinite lockdown - disable automatic AP PIN + * reenablement. + */ + eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); + wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely"); + } else if (!hapd->conf->ap_setup_locked) { + if (hapd->ap_pin_lockout_time == 0) + hapd->ap_pin_lockout_time = 60; + else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 && + (hapd->ap_pin_failures % 3) == 0) + hapd->ap_pin_lockout_time *= 2; + + wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN for %u seconds", + hapd->ap_pin_lockout_time); + eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); + eloop_register_timeout(hapd->ap_pin_lockout_time, 0, + hostapd_wps_reenable_ap_pin, hapd, + NULL); + } + + return 0; +} + + +static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, + struct wps_event_pwd_auth_fail *data) +{ + /* Update WPS Status - Authentication Failure */ + wpa_printf(MSG_DEBUG, "WPS: Authentication failure update"); + hapd->wps_stats.status = WPS_FAILURE_STATUS; + os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN); + + wps_pwd_auth_fail(hapd, data); +} + + +static int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx) +{ + if (hapd->conf->ap_pin == NULL || hapd->wps == NULL) + return 0; + + if (hapd->ap_pin_failures_consecutive == 0) + return 0; + + wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter " + "- total validation failures %u (%u consecutive)", + hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive); + hapd->ap_pin_failures_consecutive = 0; + + return 0; +} + + +static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd) +{ + wps_ap_pin_success(hapd, NULL); +} + + +static void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd) +{ + /* Update WPS Status - PBC Overlap */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP; +} + + +static void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd) +{ + /* Update WPS PBC Status:PBC Timeout */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT; +} + + +static void hostapd_wps_event_pbc_active(struct hostapd_data *hapd) +{ + /* Update WPS PBC status - Active */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE; +} + + +static void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd) +{ + /* Update WPS PBC status - Active */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE; +} + + +static void hostapd_wps_event_success(struct hostapd_data *hapd, + struct wps_event_success *success) +{ + /* Update WPS status - Success */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE; + hapd->wps_stats.status = WPS_SUCCESS_STATUS; + os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN); +} + + +static void hostapd_wps_event_fail(struct hostapd_data *hapd, + struct wps_event_fail *fail) +{ + /* Update WPS status - Failure */ + hapd->wps_stats.status = WPS_FAILURE_STATUS; + os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN); + + if (fail->error_indication > 0 && + fail->error_indication < NUM_WPS_EI_VALUES) { + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", + fail->msg, fail->config_error, fail->error_indication, + wps_ei_str(fail->error_indication)); + } else { + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPS_EVENT_FAIL "msg=%d config_error=%d", + fail->msg, fail->config_error); + } +} + + +static void hostapd_wps_event_cb(void *ctx, enum wps_event event, + union wps_event_data *data) +{ + struct hostapd_data *hapd = ctx; + + switch (event) { + case WPS_EV_M2D: + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D); + break; + case WPS_EV_FAIL: + hostapd_wps_event_fail(hapd, &data->fail); + break; + case WPS_EV_SUCCESS: + hostapd_wps_event_success(hapd, &data->success); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS); + break; + case WPS_EV_PWD_AUTH_FAIL: + hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail); + break; + case WPS_EV_PBC_OVERLAP: + hostapd_wps_event_pbc_overlap(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP); + break; + case WPS_EV_PBC_TIMEOUT: + hostapd_wps_event_pbc_timeout(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT); + break; + case WPS_EV_PBC_ACTIVE: + hostapd_wps_event_pbc_active(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE); + break; + case WPS_EV_PBC_DISABLE: + hostapd_wps_event_pbc_disable(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE); + break; + case WPS_EV_ER_AP_ADD: + break; + case WPS_EV_ER_AP_REMOVE: + break; + case WPS_EV_ER_ENROLLEE_ADD: + break; + case WPS_EV_ER_ENROLLEE_REMOVE: + break; + case WPS_EV_ER_AP_SETTINGS: + break; + case WPS_EV_ER_SET_SELECTED_REGISTRAR: + break; + case WPS_EV_AP_PIN_SUCCESS: + hostapd_wps_ap_pin_success(hapd); + break; + } + if (hapd->wps_event_cb) + hapd->wps_event_cb(hapd, event, data); +} + + +static int hostapd_wps_rf_band_cb(void *ctx) +{ + return WPS_RF_24GHZ; +} + + +static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only) +{ + esp_wifi_unset_appie_internal(WIFI_APPIE_RAM_BEACON); + esp_wifi_unset_appie_internal(WIFI_APPIE_RAM_PROBE_RSP); +} + +static void hostapd_free_wps(struct wps_context *wps) +{ + int i; + + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) + wpabuf_free(wps->dev.vendor_ext[i]); + wps_device_data_free(&wps->dev); + os_free(wps->network_key); + wpabuf_free(wps->dh_pubkey); + wpabuf_free(wps->dh_privkey); + os_free(wps); +} + +int hostapd_init_wps(struct hostapd_data *hapd, struct wps_data *wps_data, struct wps_context *wps) +{ + struct wps_registrar_config cfg; + + wps->event_cb = hostapd_wps_event_cb; + wps->rf_band_cb = hostapd_wps_rf_band_cb; + wps->cb_ctx = hapd; + + wps->wps_state = WPS_STATE_CONFIGURED; + wps->ap_setup_locked = 0; + wps->ap = 1; + + /* build credentials to be given */ + hostapd_wps_config_ap(hapd, wps_data); + + os_memset(&cfg, 0, sizeof(cfg)); + cfg.set_ie_cb = hostapd_wps_set_ie_cb; + cfg.reg_success_cb = hostapd_wps_reg_success_cb; + cfg.cb_ctx = hapd; + cfg.disable_auto_conf = 1; + + wps->registrar = wps_registrar_init(wps, &cfg); + if (wps->registrar == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar"); + goto fail; + } + + hapd->wps = wps; + + eap_server_identity_register(); + if (eap_server_wsc_register() < 0) { + wpa_printf(MSG_ERROR, "Failed to register for wsc server"); + } + if (ieee802_1x_init(hapd) < 0) { + return 0; + } + return 0; + +fail: + hostapd_free_wps(wps); + return -1; +} + + +void hostapd_deinit_wps(struct hostapd_data *hapd) +{ + eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); + eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); + if (hapd->wps == NULL) { + hostapd_wps_clear_ies(hapd, 1); + return; + } + wps_registrar_deinit(hapd->wps->registrar); + hostapd_free_wps(hapd->wps); + hapd->wps = NULL; + hostapd_wps_clear_ies(hapd, 1); +} + + +void hostapd_update_wps(struct hostapd_data *hapd) +{ + struct wps_context *wps = hapd->wps; + struct hostapd_bss_config *conf = hapd->conf; + + if (!wps) + return; + + os_memcpy(wps->ssid, conf->ssid.ssid, conf->ssid.ssid_len); + wps->ssid_len = conf->ssid.ssid_len; + + /* Clear WPS settings, then fill them again */ + hostapd_wps_config_ap(hapd, NULL); + + wps_registrar_update_ie(wps->registrar); +} + +int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *pin) +{ + int ret; + + if (hapd->wps == NULL) + return 0; + ret = wps_registrar_add_pin(hapd->wps->registrar, NULL, + NULL, pin, 8, 120); + return ret; +} + +int hostapd_wps_button_pushed(struct hostapd_data *hapd, + const u8 *p2p_dev_addr) +{ + if (hapd->wps) { + return wps_registrar_button_pushed(hapd->wps->registrar, + p2p_dev_addr); + } + return 0; +} + + +static int wps_cancel(struct hostapd_data *hapd) +{ + if (hapd->wps) { + wps_registrar_wps_cancel(hapd->wps->registrar); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_CANCEL); + } + + return 0; +} + + +int hostapd_wps_cancel(struct hostapd_data *hapd) +{ + int ret; + + ret = wps_cancel(hapd); + return ret; +} + + +static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx) +{ + struct hostapd_data *hapd = eloop_data; + wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out"); + hostapd_wps_ap_pin_disable(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED); +} + + +static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout) +{ + wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout); + hapd->ap_pin_failures = 0; + hapd->ap_pin_failures_consecutive = 0; + hapd->conf->ap_setup_locked = 0; + if (hapd->wps->ap_setup_locked) { + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED); + hapd->wps->ap_setup_locked = 0; + wps_registrar_update_ie(hapd->wps->registrar); + } + eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); + if (timeout > 0) + eloop_register_timeout(timeout, 0, + hostapd_wps_ap_pin_timeout, hapd, NULL); +} + +static int wps_ap_pin_disable(struct hostapd_data *hapd) +{ + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = NULL; + eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); + return 0; +} + + +void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN"); + wps_ap_pin_disable(hapd); +} + + +struct wps_ap_pin_data { + char pin_txt[9]; + int timeout; +}; + + +static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx) +{ + struct wps_ap_pin_data *data = ctx; + + if (!hapd->wps) + return 0; + + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = os_strdup(data->pin_txt); + hostapd_wps_ap_pin_enable(hapd, data->timeout); + return 0; +} + + +const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout) +{ + unsigned int pin; + struct wps_ap_pin_data data; + + if (wps_generate_pin(&pin) < 0) + return NULL; + os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin); + data.timeout = timeout; + wps_ap_pin_set(hapd, &data); + return hapd->conf->ap_pin; +} + +const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd) +{ + return hapd->conf->ap_pin; +} + + +int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin, + int timeout) +{ + struct wps_ap_pin_data data; + int ret; + + ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin); + if (os_snprintf_error(sizeof(data.pin_txt), ret)) + return -1; + data.timeout = timeout; + return wps_ap_pin_set(hapd, &data); +} + + +int hostapd_wps_config_ap(struct hostapd_data *hapd, struct wps_data *wps_data) +{ + struct wps_credential cred = {0}; + + os_memcpy(cred.ssid, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len); + cred.ssid_len = hapd->conf->ssid.ssid_len; + cred.encr_type = WPS_ENCR_NONE; + cred.auth_type = WPS_AUTH_OPEN; + + if (hapd->wpa_auth->conf.wpa == WPA_PROTO_WPA) { + cred.auth_type = WPS_AUTH_WPAPSK; + cred.encr_type = WPS_ENCR_TKIP; + } else if (hapd->wpa_auth->conf.wpa == WPA_PROTO_RSN) { + cred.auth_type = WPS_AUTH_WPA2PSK; + cred.encr_type = WPS_ENCR_AES; + } else if (hapd->wpa_auth->conf.wpa == (WPA_PROTO_RSN | WPA_PROTO_WPA)) { + cred.auth_type = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; + cred.encr_type = WPS_ENCR_AES; + } + + if (hapd->conf->ssid.wpa_passphrase) { + cred.key_len = os_strlen(hapd->conf->ssid.wpa_passphrase); + memcpy(cred.key, hapd->conf->ssid.wpa_passphrase, cred.key_len); + } + wps_data->use_cred = os_malloc(sizeof(struct wps_credential)); + if (!wps_data->use_cred) { + wpa_printf(MSG_ERROR, "WPS: Disabling AP PIN"); + return -1; + } + os_memcpy(wps_data->use_cred, &cred, sizeof(struct wps_credential)); + return 0; +} diff --git a/components/wpa_supplicant/src/ap/wps_hostapd.h b/components/wpa_supplicant/src/ap/wps_hostapd.h new file mode 100644 index 0000000000..8c4b584a39 --- /dev/null +++ b/components/wpa_supplicant/src/ap/wps_hostapd.h @@ -0,0 +1,78 @@ +/* + * hostapd / WPS integration + * Copyright (c) 2008-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPS_HOSTAPD_H +#define WPS_HOSTAPD_H + +#ifdef CONFIG_WPS + +int hostapd_init_wps(struct hostapd_data *hapd, struct wps_data *wps_data, struct wps_context *wps); +int hostapd_init_wps_complete(struct hostapd_data *hapd); +void hostapd_deinit_wps(struct hostapd_data *hapd); +void hostapd_update_wps(struct hostapd_data *hapd); +void hostapd_wps_eap_completed(struct hostapd_data *hapd); +int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *pin); +int hostapd_wps_button_pushed(struct hostapd_data *hapd, + const u8 *p2p_dev_addr); +int hostapd_wps_cancel(struct hostapd_data *hapd); +int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr, + char *buf, size_t buflen); +void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd); +const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout); +const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd); +int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin, + int timeout); +void hostapd_wps_update_ie(struct hostapd_data *hapd); +int hostapd_wps_config_ap(struct hostapd_data *hapd, struct wps_data *wps_data); +int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd, + const struct wpabuf *data); +#else /* CONFIG_WPS */ + +static inline int hostapd_init_wps(struct hostapd_data *hapd, struct wps_data *wps_data, struct wps_context *wps) +{ + return 0; +} + +static inline void hostapd_deinit_wps(struct hostapd_data *hapd) +{ +} + +static inline int hostapd_init_wps_complete(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void hostapd_update_wps(struct hostapd_data *hapd) +{ +} + +static inline void hostapd_wps_eap_completed(struct hostapd_data *hapd) +{ +} + +static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, + const u8 *addr, + char *buf, size_t buflen) +{ + return 0; +} + +static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd, + const u8 *p2p_dev_addr) +{ + return 0; +} + +static inline int hostapd_wps_cancel(struct hostapd_data *hapd) +{ + return 0; +} + +#endif /* CONFIG_WPS */ + +#endif /* WPS_HOSTAPD_H */ diff --git a/components/wpa_supplicant/src/common/ieee802_11_common.c b/components/wpa_supplicant/src/common/ieee802_11_common.c index 8c1af162b3..ea67cff967 100644 --- a/components/wpa_supplicant/src/common/ieee802_11_common.c +++ b/components/wpa_supplicant/src/common/ieee802_11_common.c @@ -228,6 +228,39 @@ int ieee802_11_parse_elems(struct wpa_supplicant *wpa_s, const u8 *start, size_t return 0; } +struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, + u32 oui_type) +{ + struct wpabuf *buf; + const struct element *elem, *found = NULL; + + for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) { + if (elem->datalen >= 4 && + WPA_GET_BE32(elem->data) == oui_type) { + found = elem; + break; + } + } + + if (!found) + return NULL; /* No specified vendor IE found */ + + buf = wpabuf_alloc(ies_len); + if (buf == NULL) + return NULL; + + /* + * There may be multiple vendor IEs in the message, so need to + * concatenate their data fields. + */ + for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) { + if (elem->datalen >= 4 && WPA_GET_BE32(elem->data) == oui_type) + wpabuf_put_data(buf, elem->data + 4, elem->datalen - 4); + } + + return buf; +} + int ieee802_11_ext_capab(const u8 *ie, unsigned int capab) { if (!ie || ie[1] <= capab / 8) diff --git a/components/wpa_supplicant/src/common/ieee802_11_common.h b/components/wpa_supplicant/src/common/ieee802_11_common.h index c210562c44..aa150fbf6a 100644 --- a/components/wpa_supplicant/src/common/ieee802_11_common.h +++ b/components/wpa_supplicant/src/common/ieee802_11_common.h @@ -43,4 +43,6 @@ const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type); size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len); u8 get_operating_class(u8 chan, int sec_channel); int ieee802_11_ie_count(const u8 *ies, size_t ies_len); +struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, + u32 oui_type); #endif /* IEEE802_11_COMMON_H */ diff --git a/components/wpa_supplicant/src/eap_peer/eap_defs.h b/components/wpa_supplicant/src/eap_peer/eap_defs.h index 10995d3868..da5f549c72 100644 --- a/components/wpa_supplicant/src/eap_peer/eap_defs.h +++ b/components/wpa_supplicant/src/eap_peer/eap_defs.h @@ -1,6 +1,6 @@ /* * EAP server/peer: Shared EAP definitions - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,22 +22,49 @@ struct eap_hdr { /* followed by length-4 octets of data */ } STRUCT_PACKED; - #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, - EAP_CODE_FAILURE = 4 }; + EAP_CODE_FAILURE = 4, EAP_CODE_INITIATE = 5, EAP_CODE_FINISH = 6 }; /* EAP Request and Response data begins with one octet Type. Success and * Failure do not have additional data. */ +/* Type field in EAP-Initiate and EAP-Finish messages */ +enum eap_erp_type { + EAP_ERP_TYPE_REAUTH_START = 1, + EAP_ERP_TYPE_REAUTH = 2, +}; + +/* ERP TV/TLV types */ +enum eap_erp_tlv_type { + EAP_ERP_TLV_KEYNAME_NAI = 1, + EAP_ERP_TV_RRK_LIFETIME = 2, + EAP_ERP_TV_RMSK_LIFETIME = 3, + EAP_ERP_TLV_DOMAIN_NAME = 4, + EAP_ERP_TLV_CRYPTOSUITES = 5, + EAP_ERP_TLV_AUTHORIZATION_INDICATION = 6, + EAP_ERP_TLV_CALLED_STATION_ID = 128, + EAP_ERP_TLV_CALLING_STATION_ID = 129, + EAP_ERP_TLV_NAS_IDENTIFIER = 130, + EAP_ERP_TLV_NAS_IP_ADDRESS = 131, + EAP_ERP_TLV_NAS_IPV6_ADDRESS = 132, +}; + +/* ERP Cryptosuite */ +enum eap_erp_cryptosuite { + EAP_ERP_CS_HMAC_SHA256_64 = 1, + EAP_ERP_CS_HMAC_SHA256_128 = 2, + EAP_ERP_CS_HMAC_SHA256_256 = 3, +}; + /* * EAP Method Types as allocated by IANA: * http://www.iana.org/assignments/eap-numbers */ -typedef enum { +typedef enum eap_type { EAP_TYPE_NONE = 0, EAP_TYPE_IDENTITY = 1 /* RFC 3748 */, EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */, @@ -65,6 +92,7 @@ typedef enum { EAP_TYPE_GPSK = 51 /* RFC 5433 */, EAP_TYPE_PWD = 52 /* RFC 5931 */, EAP_TYPE_EKE = 53 /* RFC 6124 */, + EAP_TYPE_TEAP = 55 /* RFC 7170 */, EAP_TYPE_EXPANDED = 254 /* RFC 3748 */ } EapType; @@ -73,8 +101,9 @@ typedef enum { enum { EAP_VENDOR_IETF = 0, EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */, - EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */, - EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */ + EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance (moved to WBA) */, + EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */, + EAP_VENDOR_WFA_NEW = 40808 /* Wi-Fi Alliance */ }; struct eap_expand { @@ -86,7 +115,11 @@ struct eap_expand { #define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP #define EAP_VENDOR_TYPE_UNAUTH_TLS 1 +#define EAP_VENDOR_WFA_UNAUTH_TLS 13 + #define EAP_MSK_LEN 64 #define EAP_EMSK_LEN 64 +#define EAP_EMSK_NAME_LEN 8 +#define ERP_MAX_KEY_LEN 64 #endif /* EAP_DEFS_H */ diff --git a/components/wpa_supplicant/src/eap_server/eap.h b/components/wpa_supplicant/src/eap_server/eap.h new file mode 100644 index 0000000000..5d18ea51b3 --- /dev/null +++ b/components/wpa_supplicant/src/eap_server/eap.h @@ -0,0 +1,297 @@ +/* + * hostapd / EAP Full Authenticator state machine (RFC 4137) + * Copyright (c) 2004-2014, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_H +#define EAP_H + +#include "common/defs.h" +#include "utils/list.h" +#include "eap_peer/eap_defs.h" +#include "eap_server/eap_methods.h" +#include "utils/wpabuf.h" + +struct eap_sm; + +#define EAP_TTLS_AUTH_PAP 1 +#define EAP_TTLS_AUTH_CHAP 2 +#define EAP_TTLS_AUTH_MSCHAP 4 +#define EAP_TTLS_AUTH_MSCHAPV2 8 + +struct eap_user { + struct { + int vendor; + u32 method; + } methods[EAP_MAX_METHODS]; + u8 *password; + size_t password_len; + int password_hash; /* whether password is hashed with + * nt_password_hash() */ + u8 *salt; + size_t salt_len; + int phase2; + int force_version; + unsigned int remediation:1; + unsigned int macacl:1; + int ttls_auth; /* bitfield of + * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */ + struct hostapd_radius_attr *accept_attr; + u32 t_c_timestamp; +}; + +struct eap_eapol_interface { + /* Lower layer to full authenticator variables */ + bool eapResp; /* shared with EAPOL Backend Authentication */ + struct wpabuf *eapRespData; + bool portEnabled; + int retransWhile; + bool eapRestart; /* shared with EAPOL Authenticator PAE */ + int eapSRTT; + int eapRTTVAR; + + /* Full authenticator to lower layer variables */ + bool eapReq; /* shared with EAPOL Backend Authentication */ + bool eapNoReq; /* shared with EAPOL Backend Authentication */ + bool eapSuccess; + bool eapFail; + bool eapTimeout; + struct wpabuf *eapReqData; + u8 *eapKeyData; + size_t eapKeyDataLen; + u8 *eapSessionId; + size_t eapSessionIdLen; + bool eapKeyAvailable; /* called keyAvailable in IEEE 802.1X-2004 */ + +#ifndef ESP_SUPPLICANT + /* AAA interface to full authenticator variables */ + bool aaaEapReq; + bool aaaEapNoReq; + bool aaaSuccess; + bool aaaFail; + struct wpabuf *aaaEapReqData; + u8 *aaaEapKeyData; + size_t aaaEapKeyDataLen; + bool aaaEapKeyAvailable; + int aaaMethodTimeout; + + /* Full authenticator to AAA interface variables */ + bool aaaEapResp; + struct wpabuf *aaaEapRespData; + /* aaaIdentity -> eap_get_identity() */ + bool aaaTimeout; +#endif +}; + +struct eap_server_erp_key { + struct dl_list list; + size_t rRK_len; + size_t rIK_len; + u8 rRK[ERP_MAX_KEY_LEN]; + u8 rIK[ERP_MAX_KEY_LEN]; + u32 recv_seq; + u8 cryptosuite; + char keyname_nai[]; +}; + +struct eapol_callbacks { + int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, + int phase2, struct eap_user *user); + const char * (*get_eap_req_id_text)(void *ctx, size_t *len); + void (*log_msg)(void *ctx, const char *msg); + int (*get_erp_send_reauth_start)(void *ctx); + const char * (*get_erp_domain)(void *ctx); + struct eap_server_erp_key * (*erp_get_key)(void *ctx, + const char *keyname); + int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp); +}; + +struct eap_config { + /** + * ssl_ctx - TLS context + * + * This is passed to the EAP server implementation as a callback + * context for TLS operations. + */ + void *ssl_ctx; + void *msg_ctx; + + /** + * eap_sim_db_priv - EAP-SIM/AKA database context + * + * This is passed to the EAP-SIM/AKA server implementation as a + * callback context. + */ + void *eap_sim_db_priv; + bool backend_auth; + int eap_server; + + /** + * pwd_group - The D-H group assigned for EAP-pwd + * + * If EAP-pwd is not used it can be set to zero. + */ + u16 pwd_group; + + /** + * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST + * + * This parameter is used to set a key for EAP-FAST to encrypt the + * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If + * set, must point to a 16-octet key. + */ + u8 *pac_opaque_encr_key; + + /** + * eap_fast_a_id - EAP-FAST authority identity (A-ID) + * + * If EAP-FAST is not used, this can be set to %NULL. In theory, this + * is a variable length field, but due to some existing implementations + * requiring A-ID to be 16 octets in length, it is recommended to use + * that length for the field to provide interoperability with deployed + * peer implementations. + */ + u8 *eap_fast_a_id; + + /** + * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets + */ + size_t eap_fast_a_id_len; + /** + * eap_fast_a_id_info - EAP-FAST authority identifier information + * + * This A-ID-Info contains a user-friendly name for the A-ID. For + * example, this could be the enterprise and server names in + * human-readable format. This field is encoded as UTF-8. If EAP-FAST + * is not used, this can be set to %NULL. + */ + char *eap_fast_a_id_info; + + /** + * eap_fast_prov - EAP-FAST provisioning modes + * + * 0 = provisioning disabled, 1 = only anonymous provisioning allowed, + * 2 = only authenticated provisioning allowed, 3 = both provisioning + * modes allowed. + */ + enum { + NO_PROV, ANON_PROV, AUTH_PROV, BOTH_PROV + } eap_fast_prov; + + /** + * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds + * + * This is the hard limit on how long a provisioned PAC-Key can be + * used. + */ + int pac_key_lifetime; + + /** + * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds + * + * This is a soft limit on the PAC-Key. The server will automatically + * generate a new PAC-Key when this number of seconds (or fewer) of the + * lifetime remains. + */ + int pac_key_refresh_time; + int eap_teap_auth; + int eap_teap_pac_no_inner; + int eap_teap_separate_result; + enum eap_teap_id { + EAP_TEAP_ID_ALLOW_ANY = 0, + EAP_TEAP_ID_REQUIRE_USER = 1, + EAP_TEAP_ID_REQUIRE_MACHINE = 2, + EAP_TEAP_ID_REQUEST_USER_ACCEPT_MACHINE = 3, + EAP_TEAP_ID_REQUEST_MACHINE_ACCEPT_USER = 4, + EAP_TEAP_ID_REQUIRE_USER_AND_MACHINE = 5, + } eap_teap_id; + + /** + * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication + * + * This controls whether the protected success/failure indication + * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA. + */ + int eap_sim_aka_result_ind; + int eap_sim_id; + + /** + * tnc - Trusted Network Connect (TNC) + * + * This controls whether TNC is enabled and will be required before the + * peer is allowed to connect. Note: This is only used with EAP-TTLS + * and EAP-FAST. If any other EAP method is enabled, the peer will be + * allowed to connect without TNC. + */ + int tnc; + + /** + * wps - Wi-Fi Protected Setup context + * + * If WPS is used with an external RADIUS server (which is quite + * unlikely configuration), this is used to provide a pointer to WPS + * context data. Normally, this can be set to %NULL. + */ + struct wps_context *wps; + int fragment_size; + + int pbc_in_m1; + + /** + * server_id - Server identity + */ + u8 *server_id; + size_t server_id_len; + + /** + * erp - Whether EAP Re-authentication Protocol (ERP) is enabled + * + * This controls whether the authentication server derives ERP key + * hierarchy (rRK and rIK) from full EAP authentication and allows + * these keys to be used to perform ERP to derive rMSK instead of full + * EAP authentication to derive MSK. + */ + int erp; + unsigned int tls_session_lifetime; + unsigned int tls_flags; + + unsigned int max_auth_rounds; + unsigned int max_auth_rounds_short; +}; + +struct eap_session_data { + const struct wpabuf *assoc_wps_ie; + const struct wpabuf *assoc_p2p_ie; + const u8 *peer_addr; +#ifdef CONFIG_TESTING_OPTIONS + u32 tls_test_flags; +#endif /* CONFIG_TESTING_OPTIONS */ +}; + + +struct eap_sm * eap_server_sm_init(void *eapol_ctx, + const struct eapol_callbacks *eapol_cb, + const struct eap_config *conf, + const struct eap_session_data *sess); +void eap_server_sm_deinit(struct eap_sm *sm); +int eap_server_sm_step(struct eap_sm *sm); +void eap_sm_notify_cached(struct eap_sm *sm); +void eap_sm_pending_cb(struct eap_sm *sm); +int eap_sm_method_pending(struct eap_sm *sm); +const u8 * eap_get_identity(struct eap_sm *sm, size_t *len); +const char * eap_get_serial_num(struct eap_sm *sm); +const char * eap_get_method(struct eap_sm *sm); +const char * eap_get_imsi(struct eap_sm *sm); +struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm); +void eap_server_clear_identity(struct eap_sm *sm); +void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source, + const u8 *username, size_t username_len, + const u8 *challenge, const u8 *response); +void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len); +void eap_user_free(struct eap_user *user); +void eap_server_config_free(struct eap_config *cfg); + +#endif /* EAP_H */ diff --git a/components/wpa_supplicant/src/eap_server/eap_i.h b/components/wpa_supplicant/src/eap_server/eap_i.h new file mode 100644 index 0000000000..016c481893 --- /dev/null +++ b/components/wpa_supplicant/src/eap_server/eap_i.h @@ -0,0 +1,203 @@ +/* + * hostapd / EAP Authenticator state machine internal structures (RFC 4137) + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_I_H +#define EAP_I_H + +#include "utils/wpabuf.h" +#include "eap_server/eap.h" +#include "eap_peer/eap_common.h" + +/* RFC 4137 - EAP Standalone Authenticator */ + +/** + * struct eap_method - EAP method interface + * This structure defines the EAP method interface. Each method will need to + * register its own EAP type, EAP name, and set of function pointers for method + * specific operations. This interface is based on section 5.4 of RFC 4137. + */ +struct eap_method { + int vendor; + enum eap_type method; + const char *name; + + void * (*init)(struct eap_sm *sm); + void * (*initPickUp)(struct eap_sm *sm); + void (*reset)(struct eap_sm *sm, void *priv); + + struct wpabuf * (*buildReq)(struct eap_sm *sm, void *priv, u8 id); + int (*getTimeout)(struct eap_sm *sm, void *priv); + bool (*check)(struct eap_sm *sm, void *priv, struct wpabuf *respData); + void (*process)(struct eap_sm *sm, void *priv, + struct wpabuf *respData); + bool (*isDone)(struct eap_sm *sm, void *priv); + u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len); + /* isSuccess is not specified in draft-ietf-eap-statemachine-05.txt, + * but it is useful in implementing Policy.getDecision() */ + bool (*isSuccess)(struct eap_sm *sm, void *priv); + + /** + * free - Free EAP method data + * @method: Pointer to the method data registered with + * eap_server_method_register(). + * + * This function will be called when the EAP method is being + * unregistered. If the EAP method allocated resources during + * registration (e.g., allocated struct eap_method), they should be + * freed in this function. No other method functions will be called + * after this call. If this function is not defined (i.e., function + * pointer is %NULL), a default handler is used to release the method + * data with free(method). This is suitable for most cases. + */ + void (*free)(struct eap_method *method); + +#define EAP_SERVER_METHOD_INTERFACE_VERSION 1 + /** + * version - Version of the EAP server method interface + * + * The EAP server method implementation should set this variable to + * EAP_SERVER_METHOD_INTERFACE_VERSION. This is used to verify that the + * EAP method is using supported API version when using dynamically + * loadable EAP methods. + */ + int version; + + /** + * next - Pointer to the next EAP method + * + * This variable is used internally in the EAP method registration code + * to create a linked list of registered EAP methods. + */ + struct eap_method *next; + + /** + * get_emsk - Get EAP method specific keying extended material (EMSK) + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to a variable to store EMSK length + * Returns: EMSK or %NULL if not available + * + * This function can be used to get the extended keying material from + * the EAP method. The key may already be stored in the method-specific + * private data or this function may derive the key. + */ + u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len); + + /** + * getSessionId - Get EAP method specific Session-Id + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to a variable to store Session-Id length + * Returns: Session-Id or %NULL if not available + * + * This function can be used to get the Session-Id from the EAP method. + * The Session-Id may already be stored in the method-specific private + * data or this function may derive the Session-Id. + */ + u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len); +}; + +/** + * struct eap_sm - EAP server state machine data + */ +struct eap_sm { + enum { + EAP_DISABLED, EAP_INITIALIZE, EAP_IDLE, EAP_RECEIVED, + EAP_INTEGRITY_CHECK, EAP_METHOD_RESPONSE, EAP_METHOD_REQUEST, + EAP_PROPOSE_METHOD, EAP_SELECT_ACTION, EAP_SEND_REQUEST, + EAP_DISCARD, EAP_NAK, EAP_RETRANSMIT, EAP_SUCCESS, EAP_FAILURE, + EAP_TIMEOUT_FAILURE, EAP_PICK_UP_METHOD, + EAP_INITIALIZE_PASSTHROUGH, EAP_IDLE2, EAP_RETRANSMIT2, + EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2, + EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE, + EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2, + EAP_INITIATE_REAUTH_START, EAP_INITIATE_RECEIVED + } EAP_state; + + /* Constants */ + int MaxRetrans; + + struct eap_eapol_interface eap_if; + + /* Full authenticator state machine local variables */ + + /* Long-term (maintained between packets) */ + enum eap_type currentMethod; + int currentId; + enum { + METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END + } methodState; + int retransCount; + struct wpabuf *lastReqData; + int methodTimeout; + + /* Short-term (not maintained between packets) */ + bool rxResp; + bool rxInitiate; + int respId; + enum eap_type respMethod; + int respVendor; + u32 respVendorMethod; + bool ignore; + enum { + DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE, + DECISION_PASSTHROUGH, DECISION_INITIATE_REAUTH_START + } decision; + + /* Miscellaneous variables */ + const struct eap_method *m; /* selected EAP method */ + /* not defined in RFC 4137 */ + bool changed; + void *eapol_ctx; + const struct eapol_callbacks *eapol_cb; + void *eap_method_priv; + u8 *identity; + size_t identity_len; + char *serial_num; + char imsi[20]; + /* Whether Phase 2 method should validate identity match */ + int require_identity_match; + int lastId; /* Identifier used in the last EAP-Packet */ + struct eap_user *user; + int user_eap_method_index; + int init_phase2; + const struct eap_config *cfg; + struct eap_config cfg_buf; + bool update_user; + + unsigned int num_rounds; + unsigned int num_rounds_short; + enum { + METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT + } method_pending; + + u8 *auth_challenge; + u8 *peer_challenge; + + struct wpabuf *assoc_wps_ie; + struct wpabuf *assoc_p2p_ie; + + bool start_reauth; + + u8 peer_addr[ETH_ALEN]; + + bool initiate_reauth_start_sent; + bool try_initiate_reauth; + +#ifdef CONFIG_TESTING_OPTIONS + u32 tls_test_flags; +#endif /* CONFIG_TESTING_OPTIONS */ +}; + +int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, + int phase2); +void eap_log_msg(struct eap_sm *sm, const char *fmt, ...) +PRINTF_FORMAT(2, 3); +void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len); + +#endif /* EAP_I_H */ diff --git a/components/wpa_supplicant/src/eap_server/eap_methods.h b/components/wpa_supplicant/src/eap_server/eap_methods.h new file mode 100644 index 0000000000..d3c2142199 --- /dev/null +++ b/components/wpa_supplicant/src/eap_server/eap_methods.h @@ -0,0 +1,52 @@ +/* + * EAP server method registration + * Copyright (c) 2004-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_SERVER_METHODS_H +#define EAP_SERVER_METHODS_H + +#include "eap_peer/eap_defs.h" + +const struct eap_method * eap_server_get_eap_method(int vendor, + enum eap_type method); +struct eap_method * eap_server_method_alloc(int version, int vendor, + enum eap_type method, + const char *name); +int eap_server_method_register(struct eap_method *method); + +enum eap_type eap_server_get_type(const char *name, int *vendor); +void eap_server_unregister_methods(void); +const char * eap_server_get_name(int vendor, enum eap_type type); + +/* EAP server method registration calls for statically linked in methods */ +int eap_server_identity_register(void); +int eap_server_md5_register(void); +int eap_server_tls_register(void); +int eap_server_unauth_tls_register(void); +int eap_server_wfa_unauth_tls_register(void); +int eap_server_mschapv2_register(void); +int eap_server_peap_register(void); +int eap_server_tlv_register(void); +int eap_server_gtc_register(void); +int eap_server_ttls_register(void); +int eap_server_sim_register(void); +int eap_server_aka_register(void); +int eap_server_aka_prime_register(void); +int eap_server_pax_register(void); +int eap_server_psk_register(void); +int eap_server_sake_register(void); +int eap_server_gpsk_register(void); +int eap_server_vendor_test_register(void); +int eap_server_fast_register(void); +int eap_server_teap_register(void); +int eap_server_wsc_register(void); +int eap_server_ikev2_register(void); +int eap_server_tnc_register(void); +int eap_server_pwd_register(void); +int eap_server_eke_register(void); + +#endif /* EAP_SERVER_METHODS_H */ diff --git a/components/wpa_supplicant/src/eap_server/eap_server.c b/components/wpa_supplicant/src/eap_server/eap_server.c new file mode 100644 index 0000000000..a0aaf8e652 --- /dev/null +++ b/components/wpa_supplicant/src/eap_server/eap_server.c @@ -0,0 +1,2118 @@ +/* + * hostapd / EAP Full Authenticator state machine (RFC 4137) + * Copyright (c) 2004-2014, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This state machine is based on the full authenticator state machine defined + * in RFC 4137. However, to support backend authentication in RADIUS + * authentication server functionality, parts of backend authenticator (also + * from RFC 4137) are mixed in. This functionality is enabled by setting + * backend_auth configuration variable to true. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/sha256.h" +#include "eap_i.h" +#include "state_machine.h" +#include "common/wpa_ctrl.h" + +#define STATE_MACHINE_DATA struct eap_sm +#define STATE_MACHINE_DEBUG_PREFIX "EAP" + +/* EAP state machines are described in RFC 4137 */ + +static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, + int eapSRTT, int eapRTTVAR, + int methodTimeout); +static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp); +#ifndef ESP_SUPPLICANT +static int eap_sm_getId(const struct wpabuf *data); +#endif +static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id); +static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id); +static int eap_sm_nextId(struct eap_sm *sm, int id); +static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list, + size_t len); +static enum eap_type eap_sm_Policy_getNextMethod(struct eap_sm *sm, + int *vendor); +static int eap_sm_Policy_getDecision(struct eap_sm *sm); +static bool eap_sm_Policy_doPickUp(struct eap_sm *sm, enum eap_type method); + + +static int eap_get_erp_send_reauth_start(struct eap_sm *sm) +{ + return 0; +} + + +static const char * eap_get_erp_domain(struct eap_sm *sm) +{ + if (sm->eapol_cb->get_erp_domain) + return sm->eapol_cb->get_erp_domain(sm->eapol_ctx); + return NULL; +} + + +#ifdef CONFIG_ERP + +static struct eap_server_erp_key * eap_erp_get_key(struct eap_sm *sm, + const char *keyname) +{ + if (sm->eapol_cb->erp_get_key) + return sm->eapol_cb->erp_get_key(sm->eapol_ctx, keyname); + return NULL; +} + + +static int eap_erp_add_key(struct eap_sm *sm, struct eap_server_erp_key *erp) +{ + if (sm->eapol_cb->erp_add_key) + return sm->eapol_cb->erp_add_key(sm->eapol_ctx, erp); + return -1; +} + +#endif /* CONFIG_ERP */ + + +static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm, + u8 id) +{ + const char *domain; + size_t plen = 1; + struct wpabuf *msg; + size_t domain_len = 0; + + domain = eap_get_erp_domain(sm); + if (domain) { + domain_len = os_strlen(domain); + plen += 2 + domain_len; + } + + msg = eap_msg_alloc(EAP_VENDOR_IETF, + (enum eap_type) EAP_ERP_TYPE_REAUTH_START, plen, + EAP_CODE_INITIATE, id); + if (msg == NULL) + return NULL; + wpabuf_put_u8(msg, 0); /* Reserved */ + if (domain) { + /* Domain name TLV */ + wpabuf_put_u8(msg, EAP_ERP_TLV_DOMAIN_NAME); + wpabuf_put_u8(msg, domain_len); + wpabuf_put_data(msg, domain, domain_len); + } + + return msg; +} + + +static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src) +{ + if (src == NULL) + return -1; + + wpabuf_free(*dst); + *dst = wpabuf_dup(src); + return *dst ? 0 : -1; +} + + +#ifndef ESP_SUPPLICANT +static int eap_copy_data(u8 **dst, size_t *dst_len, + const u8 *src, size_t src_len) +{ + if (src == NULL) + return -1; + + os_free(*dst); + *dst = os_malloc(src_len); + if (*dst) { + os_memcpy(*dst, src, src_len); + *dst_len = src_len; + return 0; + } else { + *dst_len = 0; + return -1; + } +} +#endif + +#define EAP_COPY(dst, src) \ + eap_copy_data((dst), (dst ## Len), (src), (src ## Len)) + + +/** + * eap_user_get - Fetch user information from the database + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * @identity: Identity (User-Name) of the user + * @identity_len: Length of identity in bytes + * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user + * Returns: 0 on success, or -1 on failure + * + * This function is used to fetch user information for EAP. The user will be + * selected based on the specified identity. sm->user and + * sm->user_eap_method_index are updated for the new user when a matching user + * is found. sm->user can be used to get user information (e.g., password). + */ +int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, + int phase2) +{ + struct eap_user *user; + + if (sm == NULL || sm->eapol_cb == NULL || + sm->eapol_cb->get_eap_user == NULL) + return -1; + + eap_user_free(sm->user); + sm->user = NULL; + + user = os_zalloc(sizeof(*user)); + if (user == NULL) + return -1; + + if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity, + identity_len, phase2, user) != 0) { + eap_user_free(user); + return -1; + } + + sm->user = user; + sm->user_eap_method_index = 0; + + return 0; +} + + +void eap_log_msg(struct eap_sm *sm, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + + if (sm == NULL || sm->eapol_cb == NULL || sm->eapol_cb->log_msg == NULL) + return; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + buf = os_malloc(buflen); + if (buf == NULL) + return; + va_start(ap, fmt); + vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + + sm->eapol_cb->log_msg(sm->eapol_ctx, buf); + + os_free(buf); +} + + +SM_STATE(EAP, DISABLED) +{ + SM_ENTRY(EAP, DISABLED); + sm->num_rounds = 0; + sm->num_rounds_short = 0; +} + + +SM_STATE(EAP, INITIALIZE) +{ + SM_ENTRY(EAP, INITIALIZE); + + if (sm->eap_if.eapRestart && !sm->cfg->eap_server && sm->identity) { + /* + * Need to allow internal Identity method to be used instead + * of passthrough at the beginning of reauthentication. + */ + eap_server_clear_identity(sm); + } + + sm->try_initiate_reauth = false; + sm->currentId = -1; + sm->eap_if.eapSuccess = false; + sm->eap_if.eapFail = false; + sm->eap_if.eapTimeout = false; + bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); + sm->eap_if.eapKeyData = NULL; + sm->eap_if.eapKeyDataLen = 0; + os_free(sm->eap_if.eapSessionId); + sm->eap_if.eapSessionId = NULL; + sm->eap_if.eapSessionIdLen = 0; + sm->eap_if.eapKeyAvailable = false; + sm->eap_if.eapRestart = false; + + /* + * This is not defined in RFC 4137, but method state needs to be + * reseted here so that it does not remain in success state when + * re-authentication starts. + */ + if (sm->m && sm->eap_method_priv) { + sm->m->reset(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + } + sm->m = NULL; + sm->user_eap_method_index = 0; + + if (sm->cfg->backend_auth) { + sm->currentMethod = EAP_TYPE_NONE; + /* parse rxResp, respId, respMethod */ + eap_sm_parseEapResp(sm, sm->eap_if.eapRespData); + if (sm->rxResp) { + sm->currentId = sm->respId; + } + } + sm->num_rounds = 0; + sm->num_rounds_short = 0; + sm->method_pending = METHOD_PENDING_NONE; + + wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED + MACSTR, MAC2STR(sm->peer_addr)); +} + + +SM_STATE(EAP, PICK_UP_METHOD) +{ + SM_ENTRY(EAP, PICK_UP_METHOD); + + if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) { + sm->currentMethod = sm->respMethod; + if (sm->m && sm->eap_method_priv) { + sm->m->reset(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + } + sm->m = eap_server_get_eap_method(EAP_VENDOR_IETF, + sm->currentMethod); + if (sm->m && sm->m->initPickUp) { + sm->eap_method_priv = sm->m->initPickUp(sm); + if (sm->eap_method_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP: Failed to " + "initialize EAP method %d", + sm->currentMethod); + sm->m = NULL; + sm->currentMethod = EAP_TYPE_NONE; + } + } else { + sm->m = NULL; + sm->currentMethod = EAP_TYPE_NONE; + } + } + + wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD + "method=%u", sm->currentMethod); +} + + +SM_STATE(EAP, IDLE) +{ + SM_ENTRY(EAP, IDLE); + + sm->eap_if.retransWhile = eap_sm_calculateTimeout( + sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR, + sm->methodTimeout); +} + + +SM_STATE(EAP, RETRANSMIT) +{ + SM_ENTRY(EAP, RETRANSMIT); + + sm->retransCount++; + if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) { + if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0) + sm->eap_if.eapReq = true; + } + + wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT MACSTR, + MAC2STR(sm->peer_addr)); +} + + +SM_STATE(EAP, RECEIVED) +{ + SM_ENTRY(EAP, RECEIVED); + + /* parse rxResp, respId, respMethod */ + eap_sm_parseEapResp(sm, sm->eap_if.eapRespData); + sm->num_rounds++; + if (!sm->eap_if.eapRespData || wpabuf_len(sm->eap_if.eapRespData) < 20) + sm->num_rounds_short++; + else + sm->num_rounds_short = 0; +} + + +SM_STATE(EAP, DISCARD) +{ + SM_ENTRY(EAP, DISCARD); + sm->eap_if.eapResp = false; + sm->eap_if.eapNoReq = true; +} + + +SM_STATE(EAP, SEND_REQUEST) +{ + SM_ENTRY(EAP, SEND_REQUEST); + + sm->retransCount = 0; + if (sm->eap_if.eapReqData) { + if (wpabuf_len(sm->eap_if.eapReqData) >= 20) + sm->num_rounds_short = 0; + if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0) + { + sm->eap_if.eapResp = false; + sm->eap_if.eapReq = true; + } else { + sm->eap_if.eapResp = false; + sm->eap_if.eapReq = false; + } + } else { + wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData"); + sm->eap_if.eapResp = false; + sm->eap_if.eapReq = false; + sm->eap_if.eapNoReq = true; + } +} + + +SM_STATE(EAP, INTEGRITY_CHECK) +{ + SM_ENTRY(EAP, INTEGRITY_CHECK); + + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) { + sm->ignore = true; + return; + } + + if (sm->m->check) { + sm->ignore = sm->m->check(sm, sm->eap_method_priv, + sm->eap_if.eapRespData); + } +} + + +SM_STATE(EAP, METHOD_REQUEST) +{ + SM_ENTRY(EAP, METHOD_REQUEST); + + if (sm->m == NULL) { + wpa_printf(MSG_DEBUG, "EAP: method not initialized"); + return; + } + + sm->currentId = eap_sm_nextId(sm, sm->currentId); + wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d", + sm->currentId); + sm->lastId = sm->currentId; + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = sm->m->buildReq(sm, sm->eap_method_priv, + sm->currentId); + if (sm->m->getTimeout) + sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv); + else + sm->methodTimeout = 0; +} + + +static void eap_server_erp_init(struct eap_sm *sm) +{ +#ifdef CONFIG_ERP + u8 *emsk = NULL; + size_t emsk_len = 0; + u8 EMSKname[EAP_EMSK_NAME_LEN]; + u8 len[2], ctx[3]; + const char *domain; + size_t domain_len, nai_buf_len; + struct eap_server_erp_key *erp = NULL; + int pos; + + domain = eap_get_erp_domain(sm); + if (!domain) + return; + + domain_len = os_strlen(domain); + + nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + domain_len; + if (nai_buf_len > 253) { + /* + * keyName-NAI has a maximum length of 253 octet to fit in + * RADIUS attributes. + */ + wpa_printf(MSG_DEBUG, + "EAP: Too long realm for ERP keyName-NAI maximum length"); + return; + } + nai_buf_len++; /* null termination */ + erp = os_zalloc(sizeof(*erp) + nai_buf_len); + if (erp == NULL) + goto fail; + erp->recv_seq = (u32) -1; + + emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len); + if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) { + wpa_printf(MSG_DEBUG, + "EAP: No suitable EMSK available for ERP"); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len); + + WPA_PUT_BE16(len, EAP_EMSK_NAME_LEN); + if (hmac_sha256_kdf(sm->eap_if.eapSessionId, sm->eap_if.eapSessionIdLen, + "EMSK", len, sizeof(len), + EMSKname, EAP_EMSK_NAME_LEN) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN); + + pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len, + EMSKname, EAP_EMSK_NAME_LEN); + erp->keyname_nai[pos] = '@'; + os_memcpy(&erp->keyname_nai[pos + 1], domain, domain_len); + + WPA_PUT_BE16(len, emsk_len); + if (hmac_sha256_kdf(emsk, emsk_len, + "EAP Re-authentication Root Key@ietf.org", + len, sizeof(len), erp->rRK, emsk_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP"); + goto fail; + } + erp->rRK_len = emsk_len; + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len); + + ctx[0] = EAP_ERP_CS_HMAC_SHA256_128; + WPA_PUT_BE16(&ctx[1], erp->rRK_len); + if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, + "Re-authentication Integrity Key@ietf.org", + ctx, sizeof(ctx), erp->rIK, erp->rRK_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP"); + goto fail; + } + erp->rIK_len = erp->rRK_len; + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len); + + if (eap_erp_add_key(sm, erp) == 0) { + wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s", + erp->keyname_nai); + erp = NULL; + } + +fail: + bin_clear_free(emsk, emsk_len); + bin_clear_free(erp, sizeof(*erp)); +#endif /* CONFIG_ERP */ +} + + +SM_STATE(EAP, METHOD_RESPONSE) +{ + SM_ENTRY(EAP, METHOD_RESPONSE); + + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) + return; + + sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData); + if (sm->m->isDone(sm, sm->eap_method_priv)) { + eap_sm_Policy_update(sm, NULL, 0); + bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); + if (sm->m->getKey) { + sm->eap_if.eapKeyData = sm->m->getKey( + sm, sm->eap_method_priv, + &sm->eap_if.eapKeyDataLen); + } else { + sm->eap_if.eapKeyData = NULL; + sm->eap_if.eapKeyDataLen = 0; + } + os_free(sm->eap_if.eapSessionId); + sm->eap_if.eapSessionId = NULL; + if (sm->m->getSessionId) { + sm->eap_if.eapSessionId = sm->m->getSessionId( + sm, sm->eap_method_priv, + &sm->eap_if.eapSessionIdLen); + wpa_hexdump(MSG_DEBUG, "EAP: Session-Id", + sm->eap_if.eapSessionId, + sm->eap_if.eapSessionIdLen); + } + if (sm->cfg->erp && sm->m->get_emsk && sm->eap_if.eapSessionId) + eap_server_erp_init(sm); + sm->methodState = METHOD_END; + } else { + sm->methodState = METHOD_CONTINUE; + } +} + + +SM_STATE(EAP, PROPOSE_METHOD) +{ + int vendor; + enum eap_type type; + + SM_ENTRY(EAP, PROPOSE_METHOD); + + sm->try_initiate_reauth = false; +try_another_method: + type = eap_sm_Policy_getNextMethod(sm, &vendor); + if (vendor == EAP_VENDOR_IETF) + sm->currentMethod = type; + else + sm->currentMethod = EAP_TYPE_EXPANDED; + if (sm->m && sm->eap_method_priv) { + sm->m->reset(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + } + sm->m = eap_server_get_eap_method(vendor, type); + if (sm->m) { + sm->eap_method_priv = sm->m->init(sm); + if (sm->eap_method_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP " + "method %d", sm->currentMethod); + sm->m = NULL; + sm->currentMethod = EAP_TYPE_NONE; + goto try_another_method; + } + } + if (sm->m == NULL) { + wpa_printf(MSG_DEBUG, "EAP: Could not find suitable EAP method"); + eap_log_msg(sm, "Could not find suitable EAP method"); + sm->decision = DECISION_FAILURE; + return; + } + if (sm->currentMethod == EAP_TYPE_IDENTITY || + sm->currentMethod == EAP_TYPE_NOTIFICATION) + sm->methodState = METHOD_CONTINUE; + else + sm->methodState = METHOD_PROPOSED; + + wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD + "vendor=%u method=%u", vendor, sm->currentMethod); + eap_log_msg(sm, "Propose EAP method vendor=%u method=%u", + vendor, sm->currentMethod); +} + + +SM_STATE(EAP, NAK) +{ + const struct eap_hdr *nak; + size_t len = 0; + const u8 *pos; + const u8 *nak_list = NULL; + + SM_ENTRY(EAP, NAK); + + if (sm->eap_method_priv) { + sm->m->reset(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + } + sm->m = NULL; + + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) + return; + + nak = wpabuf_head(sm->eap_if.eapRespData); + if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) { + len = be_to_host16(nak->length); + if (len > wpabuf_len(sm->eap_if.eapRespData)) + len = wpabuf_len(sm->eap_if.eapRespData); + pos = (const u8 *) (nak + 1); + len -= sizeof(*nak); + if (*pos == EAP_TYPE_NAK) { + pos++; + len--; + nak_list = pos; + } + } + eap_sm_Policy_update(sm, nak_list, len); +} + + +SM_STATE(EAP, SELECT_ACTION) +{ + SM_ENTRY(EAP, SELECT_ACTION); + + sm->decision = eap_sm_Policy_getDecision(sm); +} + + +SM_STATE(EAP, TIMEOUT_FAILURE) +{ + SM_ENTRY(EAP, TIMEOUT_FAILURE); + + sm->eap_if.eapTimeout = true; + + wpa_msg(sm->cfg->msg_ctx, MSG_INFO, + WPA_EVENT_EAP_TIMEOUT_FAILURE MACSTR, MAC2STR(sm->peer_addr)); +} + + +SM_STATE(EAP, FAILURE) +{ + SM_ENTRY(EAP, FAILURE); + + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = eap_sm_buildFailure(sm, sm->currentId); + wpabuf_free(sm->lastReqData); + sm->lastReqData = NULL; + sm->eap_if.eapFail = true; + + wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE + MACSTR, MAC2STR(sm->peer_addr)); +} + + +SM_STATE(EAP, SUCCESS) +{ + SM_ENTRY(EAP, SUCCESS); + + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = eap_sm_buildSuccess(sm, sm->currentId); + wpabuf_free(sm->lastReqData); + sm->lastReqData = NULL; + if (sm->eap_if.eapKeyData) + sm->eap_if.eapKeyAvailable = true; + sm->eap_if.eapSuccess = true; + + wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + MACSTR, MAC2STR(sm->peer_addr)); +} + + +SM_STATE(EAP, INITIATE_REAUTH_START) +{ + SM_ENTRY(EAP, INITIATE_REAUTH_START); + + sm->initiate_reauth_start_sent = true; + sm->try_initiate_reauth = true; + sm->currentId = eap_sm_nextId(sm, sm->currentId); + wpa_printf(MSG_DEBUG, + "EAP: building EAP-Initiate-Re-auth-Start: Identifier %d", + sm->currentId); + sm->lastId = sm->currentId; + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = eap_sm_buildInitiateReauthStart(sm, + sm->currentId); + wpabuf_free(sm->lastReqData); + sm->lastReqData = NULL; +} + + +#ifdef CONFIG_ERP + +static void erp_send_finish_reauth(struct eap_sm *sm, + struct eap_server_erp_key *erp, u8 id, + u8 flags, u16 seq, const char *nai) +{ + size_t plen; + struct wpabuf *msg; + u8 hash[SHA256_MAC_LEN]; + size_t hash_len; + u8 seed[4]; + + if (erp) { + switch (erp->cryptosuite) { + case EAP_ERP_CS_HMAC_SHA256_256: + hash_len = 32; + break; + case EAP_ERP_CS_HMAC_SHA256_128: + hash_len = 16; + break; + default: + return; + } + } else + hash_len = 0; + + plen = 1 + 2 + 2 + os_strlen(nai); + if (hash_len) + plen += 1 + hash_len; + msg = eap_msg_alloc(EAP_VENDOR_IETF, + (enum eap_type) EAP_ERP_TYPE_REAUTH, + plen, EAP_CODE_FINISH, id); + if (msg == NULL) + return; + wpabuf_put_u8(msg, flags); + wpabuf_put_be16(msg, seq); + + wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI); + wpabuf_put_u8(msg, os_strlen(nai)); + wpabuf_put_str(msg, nai); + + if (erp) { + wpabuf_put_u8(msg, erp->cryptosuite); + if (hmac_sha256(erp->rIK, erp->rIK_len, + wpabuf_head(msg), wpabuf_len(msg), hash) < 0) { + wpabuf_free(msg); + return; + } + wpabuf_put_data(msg, hash, hash_len); + } + + wpa_printf(MSG_DEBUG, "EAP: Send EAP-Finish/Re-auth (%s)", + flags & 0x80 ? "failure" : "success"); + + sm->lastId = sm->currentId; + sm->currentId = id; + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = msg; + wpabuf_free(sm->lastReqData); + sm->lastReqData = NULL; + + if ((flags & 0x80) || !erp) { + sm->eap_if.eapFail = true; + wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE + MACSTR, MAC2STR(sm->peer_addr)); + return; + } + + bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); + sm->eap_if.eapKeyDataLen = 0; + sm->eap_if.eapKeyData = os_malloc(erp->rRK_len); + if (!sm->eap_if.eapKeyData) + return; + + WPA_PUT_BE16(seed, seq); + WPA_PUT_BE16(&seed[2], erp->rRK_len); + if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, + "Re-authentication Master Session Key@ietf.org", + seed, sizeof(seed), + sm->eap_if.eapKeyData, erp->rRK_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP"); + bin_clear_free(sm->eap_if.eapKeyData, erp->rRK_len); + sm->eap_if.eapKeyData = NULL; + return; + } + sm->eap_if.eapKeyDataLen = erp->rRK_len; + sm->eap_if.eapKeyAvailable = true; + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK", + sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); + sm->eap_if.eapSuccess = true; + + wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + MACSTR, MAC2STR(sm->peer_addr)); +} + + +SM_STATE(EAP, INITIATE_RECEIVED) +{ + const u8 *pos, *end, *start, *tlvs, *hdr; + const struct eap_hdr *ehdr; + size_t len; + u8 flags; + u16 seq; + char nai[254]; + struct eap_server_erp_key *erp; + int max_len; + u8 hash[SHA256_MAC_LEN]; + size_t hash_len; + struct erp_tlvs parse; + u8 resp_flags = 0x80; /* default to failure; cleared on success */ + + SM_ENTRY(EAP, INITIATE_RECEIVED); + + sm->rxInitiate = false; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, + (enum eap_type) EAP_ERP_TYPE_REAUTH, + sm->eap_if.eapRespData, &len); + if (pos == NULL) { + wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame"); + goto fail; + } + hdr = wpabuf_head(sm->eap_if.eapRespData); + ehdr = wpabuf_head(sm->eap_if.eapRespData); + + wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth", pos, len); + if (len < 4) { + wpa_printf(MSG_INFO, "EAP: Too short EAP-Initiate/Re-auth"); + goto fail; + } + end = pos + len; + + flags = *pos++; + seq = WPA_GET_BE16(pos); + pos += 2; + wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq); + tlvs = pos; + + /* + * Parse TVs/TLVs. Since we do not yet know the length of the + * Authentication Tag, stop parsing if an unknown TV/TLV is seen and + * just try to find the keyName-NAI first so that we can check the + * Authentication Tag. + */ + if (erp_parse_tlvs(tlvs, end, &parse, 1) < 0) + goto fail; + + if (!parse.keyname) { + wpa_printf(MSG_DEBUG, + "EAP: No keyName-NAI in EAP-Initiate/Re-auth Packet"); + goto fail; + } + + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth - keyName-NAI", + parse.keyname, parse.keyname_len); + if (parse.keyname_len > 253) { + wpa_printf(MSG_DEBUG, + "EAP: Too long keyName-NAI in EAP-Initiate/Re-auth"); + goto fail; + } + os_memcpy(nai, parse.keyname, parse.keyname_len); + nai[parse.keyname_len] = '\0'; + + if (!sm->cfg->eap_server) { + /* + * In passthrough case, EAP-Initiate/Re-auth replaces + * EAP Identity exchange. Use keyName-NAI as the user identity + * and forward EAP-Initiate/Re-auth to the backend + * authentication server. + */ + wpa_printf(MSG_DEBUG, + "EAP: Use keyName-NAI as user identity for backend authentication"); + eap_server_clear_identity(sm); + sm->identity = (u8 *) dup_binstr(parse.keyname, + parse.keyname_len); + if (!sm->identity) + goto fail; + sm->identity_len = parse.keyname_len; + return; + } + + erp = eap_erp_get_key(sm, nai); + if (!erp) { + wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s", + nai); + goto report_error; + } + + if (erp->recv_seq != (u32) -1 && erp->recv_seq >= seq) { + wpa_printf(MSG_DEBUG, + "EAP: SEQ=%u replayed (already received SEQ=%u)", + seq, erp->recv_seq); + goto fail; + } + + /* Is there enough room for Cryptosuite and Authentication Tag? */ + start = parse.keyname + parse.keyname_len; + max_len = end - start; + if (max_len < + 1 + (erp->cryptosuite == EAP_ERP_CS_HMAC_SHA256_256 ? 32 : 16)) { + wpa_printf(MSG_DEBUG, + "EAP: Not enough room for Authentication Tag"); + goto fail; + } + + switch (erp->cryptosuite) { + case EAP_ERP_CS_HMAC_SHA256_256: + if (end[-33] != erp->cryptosuite) { + wpa_printf(MSG_DEBUG, + "EAP: Different Cryptosuite used"); + goto fail; + } + hash_len = 32; + break; + case EAP_ERP_CS_HMAC_SHA256_128: + if (end[-17] != erp->cryptosuite) { + wpa_printf(MSG_DEBUG, + "EAP: Different Cryptosuite used"); + goto fail; + } + hash_len = 16; + break; + default: + hash_len = 0; + break; + } + + if (hash_len) { + if (hmac_sha256(erp->rIK, erp->rIK_len, hdr, + end - hdr - hash_len, hash) < 0) + goto fail; + if (os_memcmp(end - hash_len, hash, hash_len) != 0) { + wpa_printf(MSG_DEBUG, + "EAP: Authentication Tag mismatch"); + goto fail; + } + } + + /* Check if any supported CS results in matching tag */ + if (!hash_len && max_len >= 1 + 32 && + end[-33] == EAP_ERP_CS_HMAC_SHA256_256) { + if (hmac_sha256(erp->rIK, erp->rIK_len, hdr, + end - hdr - 32, hash) < 0) + goto fail; + if (os_memcmp(end - 32, hash, 32) == 0) { + wpa_printf(MSG_DEBUG, + "EAP: Authentication Tag match using HMAC-SHA256-256"); + hash_len = 32; + erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_256; + } + } + + if (!hash_len && end[-17] == EAP_ERP_CS_HMAC_SHA256_128) { + if (hmac_sha256(erp->rIK, erp->rIK_len, hdr, + end - hdr - 16, hash) < 0) + goto fail; + if (os_memcmp(end - 16, hash, 16) == 0) { + wpa_printf(MSG_DEBUG, + "EAP: Authentication Tag match using HMAC-SHA256-128"); + hash_len = 16; + erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_128; + } + } + + if (!hash_len) { + wpa_printf(MSG_DEBUG, + "EAP: No supported cryptosuite matched Authentication Tag"); + goto fail; + } + end -= 1 + hash_len; + + /* + * Parse TVs/TLVs again now that we know the exact part of the buffer + * that contains them. + */ + wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth TVs/TLVs", + tlvs, end - tlvs); + if (erp_parse_tlvs(tlvs, end, &parse, 0) < 0) + goto fail; + + wpa_printf(MSG_DEBUG, "EAP: ERP key %s SEQ updated to %u", + erp->keyname_nai, seq); + erp->recv_seq = seq; + resp_flags &= ~0x80; /* R=0 - success */ + +report_error: + erp_send_finish_reauth(sm, erp, ehdr->identifier, resp_flags, seq, nai); + return; + +fail: + sm->ignore = true; +} + +#endif /* CONFIG_ERP */ + + +#ifndef ESP_SUPPLICANT +SM_STATE(EAP, INITIALIZE_PASSTHROUGH) +{ + SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH); + + wpabuf_free(sm->eap_if.aaaEapRespData); + sm->eap_if.aaaEapRespData = NULL; + sm->try_initiate_reauth = false; +} + + +SM_STATE(EAP, IDLE2) +{ + SM_ENTRY(EAP, IDLE2); + + sm->eap_if.retransWhile = eap_sm_calculateTimeout( + sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR, + sm->methodTimeout); +} + + +SM_STATE(EAP, RETRANSMIT2) +{ + SM_ENTRY(EAP, RETRANSMIT2); + + sm->retransCount++; + if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) { + if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0) + sm->eap_if.eapReq = true; + } + + wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT2 MACSTR, + MAC2STR(sm->peer_addr)); +} + + +SM_STATE(EAP, RECEIVED2) +{ + SM_ENTRY(EAP, RECEIVED2); + + /* parse rxResp, respId, respMethod */ + eap_sm_parseEapResp(sm, sm->eap_if.eapRespData); +} + + +SM_STATE(EAP, DISCARD2) +{ + SM_ENTRY(EAP, DISCARD2); + sm->eap_if.eapResp = false; + sm->eap_if.eapNoReq = true; +} + + +SM_STATE(EAP, SEND_REQUEST2) +{ + SM_ENTRY(EAP, SEND_REQUEST2); + + sm->retransCount = 0; + if (sm->eap_if.eapReqData) { + if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0) + { + sm->eap_if.eapResp = false; + sm->eap_if.eapReq = true; + } else { + sm->eap_if.eapResp = false; + sm->eap_if.eapReq = false; + } + } else { + wpa_printf(MSG_INFO, "EAP: SEND_REQUEST2 - no eapReqData"); + sm->eap_if.eapResp = false; + sm->eap_if.eapReq = false; + sm->eap_if.eapNoReq = true; + } +} + + +SM_STATE(EAP, AAA_REQUEST) +{ + SM_ENTRY(EAP, AAA_REQUEST); + + if (sm->eap_if.eapRespData == NULL) { + wpa_printf(MSG_INFO, "EAP: AAA_REQUEST - no eapRespData"); + return; + } + + /* + * if (respMethod == IDENTITY) + * aaaIdentity = eapRespData + * This is already taken care of by the EAP-Identity method which + * stores the identity into sm->identity. + */ + + eap_copy_buf(&sm->eap_if.aaaEapRespData, sm->eap_if.eapRespData); +} + + +SM_STATE(EAP, AAA_RESPONSE) +{ + SM_ENTRY(EAP, AAA_RESPONSE); + + eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); + sm->currentId = eap_sm_getId(sm->eap_if.eapReqData); + sm->methodTimeout = sm->eap_if.aaaMethodTimeout; +} + + +SM_STATE(EAP, AAA_IDLE) +{ + SM_ENTRY(EAP, AAA_IDLE); + + sm->eap_if.aaaFail = false; + sm->eap_if.aaaSuccess = false; + sm->eap_if.aaaEapReq = false; + sm->eap_if.aaaEapNoReq = false; + sm->eap_if.aaaEapResp = true; +} + + +SM_STATE(EAP, TIMEOUT_FAILURE2) +{ + SM_ENTRY(EAP, TIMEOUT_FAILURE2); + + sm->eap_if.eapTimeout = true; + + wpa_msg(sm->cfg->msg_ctx, MSG_INFO, + WPA_EVENT_EAP_TIMEOUT_FAILURE2 MACSTR, MAC2STR(sm->peer_addr)); +} + + +SM_STATE(EAP, FAILURE2) +{ + SM_ENTRY(EAP, FAILURE2); + + eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); + sm->eap_if.eapFail = true; + + wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE2 MACSTR, + MAC2STR(sm->peer_addr)); +} + + +SM_STATE(EAP, SUCCESS2) +{ + SM_ENTRY(EAP, SUCCESS2); + + eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); + + sm->eap_if.eapKeyAvailable = sm->eap_if.aaaEapKeyAvailable; + if (sm->eap_if.aaaEapKeyAvailable) { + EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData); + } else { + bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); + sm->eap_if.eapKeyData = NULL; + sm->eap_if.eapKeyDataLen = 0; + } + + sm->eap_if.eapSuccess = true; + + /* + * Start reauthentication with identity request even though we know the + * previously used identity. This is needed to get reauthentication + * started properly. + */ + sm->start_reauth = true; + + wpa_msg(sm->cfg->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS2 MACSTR, + MAC2STR(sm->peer_addr)); +} +#endif + + +SM_STEP(EAP) +{ + if (sm->eap_if.eapRestart && sm->eap_if.portEnabled) + SM_ENTER_GLOBAL(EAP, INITIALIZE); + else if (!sm->eap_if.portEnabled) + SM_ENTER_GLOBAL(EAP, DISABLED); + else if (sm->num_rounds > sm->cfg->max_auth_rounds) { + if (sm->num_rounds == sm->cfg->max_auth_rounds + 1) { + wpa_printf(MSG_DEBUG, "EAP: more than %d " + "authentication rounds - abort", + sm->cfg->max_auth_rounds); + sm->num_rounds++; + SM_ENTER_GLOBAL(EAP, FAILURE); + } + } else if (sm->num_rounds_short > sm->cfg->max_auth_rounds_short) { + if (sm->num_rounds_short == + sm->cfg->max_auth_rounds_short + 1) { + wpa_printf(MSG_DEBUG, + "EAP: more than %d authentication rounds (short) - abort", + sm->cfg->max_auth_rounds_short); + sm->num_rounds_short++; + SM_ENTER_GLOBAL(EAP, FAILURE); + } + } else switch (sm->EAP_state) { + case EAP_INITIALIZE: + if (sm->cfg->backend_auth) { + if (!sm->rxResp) + SM_ENTER(EAP, SELECT_ACTION); + else if (sm->rxResp && + (sm->respMethod == EAP_TYPE_NAK || + (sm->respMethod == EAP_TYPE_EXPANDED && + sm->respVendor == EAP_VENDOR_IETF && + sm->respVendorMethod == EAP_TYPE_NAK))) + SM_ENTER(EAP, NAK); + else + SM_ENTER(EAP, PICK_UP_METHOD); + } else { + SM_ENTER(EAP, SELECT_ACTION); + } + break; + case EAP_PICK_UP_METHOD: + if (sm->currentMethod == EAP_TYPE_NONE) { + SM_ENTER(EAP, SELECT_ACTION); + } else { + SM_ENTER(EAP, METHOD_RESPONSE); + } + break; + case EAP_DISABLED: + if (sm->eap_if.portEnabled) + SM_ENTER(EAP, INITIALIZE); + break; + case EAP_IDLE: + if (sm->eap_if.retransWhile == 0) { + if (sm->try_initiate_reauth) { + sm->try_initiate_reauth = false; + SM_ENTER(EAP, SELECT_ACTION); + } else { + SM_ENTER(EAP, RETRANSMIT); + } + } else if (sm->eap_if.eapResp) + SM_ENTER(EAP, RECEIVED); + break; + case EAP_RETRANSMIT: + if (sm->retransCount > sm->MaxRetrans) + SM_ENTER(EAP, TIMEOUT_FAILURE); + else + SM_ENTER(EAP, IDLE); + break; + case EAP_RECEIVED: + if (sm->rxResp && (sm->respId == sm->currentId) && + (sm->respMethod == EAP_TYPE_NAK || + (sm->respMethod == EAP_TYPE_EXPANDED && + sm->respVendor == EAP_VENDOR_IETF && + sm->respVendorMethod == EAP_TYPE_NAK)) + && (sm->methodState == METHOD_PROPOSED)) + SM_ENTER(EAP, NAK); + else if (sm->rxResp && (sm->respId == sm->currentId) && + ((sm->respMethod == sm->currentMethod) || + (sm->respMethod == EAP_TYPE_EXPANDED && + sm->respVendor == EAP_VENDOR_IETF && + sm->respVendorMethod == sm->currentMethod))) + SM_ENTER(EAP, INTEGRITY_CHECK); +#ifdef CONFIG_ERP + else if (sm->rxInitiate) + SM_ENTER(EAP, INITIATE_RECEIVED); +#endif /* CONFIG_ERP */ + else { + wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: " + "rxResp=%d respId=%d currentId=%d " + "respMethod=%d currentMethod=%d", + sm->rxResp, sm->respId, sm->currentId, + sm->respMethod, sm->currentMethod); + eap_log_msg(sm, "Discard received EAP message"); + SM_ENTER(EAP, DISCARD); + } + break; + case EAP_DISCARD: + SM_ENTER(EAP, IDLE); + break; + case EAP_SEND_REQUEST: + SM_ENTER(EAP, IDLE); + break; + case EAP_INTEGRITY_CHECK: + if (sm->ignore) + SM_ENTER(EAP, DISCARD); + else + SM_ENTER(EAP, METHOD_RESPONSE); + break; + case EAP_METHOD_REQUEST: + if (sm->m == NULL) { + /* + * This transition is not mentioned in RFC 4137, but it + * is needed to handle cleanly a case where EAP method + * initialization fails. + */ + SM_ENTER(EAP, FAILURE); + break; + } + SM_ENTER(EAP, SEND_REQUEST); + if (sm->eap_if.eapNoReq && !sm->eap_if.eapReq) { + /* + * This transition is not mentioned in RFC 4137, but it + * is needed to handle cleanly a case where EAP method + * buildReq fails. + */ + wpa_printf(MSG_DEBUG, + "EAP: Method did not return a request"); + SM_ENTER(EAP, FAILURE); + break; + } + break; + case EAP_METHOD_RESPONSE: + /* + * Note: Mechanism to allow EAP methods to wait while going + * through pending processing is an extension to RFC 4137 + * which only defines the transits to SELECT_ACTION and + * METHOD_REQUEST from this METHOD_RESPONSE state. + */ + if (sm->methodState == METHOD_END) + SM_ENTER(EAP, SELECT_ACTION); + else if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP: Method has pending " + "processing - wait before proceeding to " + "METHOD_REQUEST state"); + } else if (sm->method_pending == METHOD_PENDING_CONT) { + wpa_printf(MSG_DEBUG, "EAP: Method has completed " + "pending processing - reprocess pending " + "EAP message"); + sm->method_pending = METHOD_PENDING_NONE; + SM_ENTER(EAP, METHOD_RESPONSE); + } else + SM_ENTER(EAP, METHOD_REQUEST); + break; + case EAP_PROPOSE_METHOD: + /* + * Note: Mechanism to allow EAP methods to wait while going + * through pending processing is an extension to RFC 4137 + * which only defines the transit to METHOD_REQUEST from this + * PROPOSE_METHOD state. + */ + if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP: Method has pending " + "processing - wait before proceeding to " + "METHOD_REQUEST state"); + if (sm->user_eap_method_index > 0) + sm->user_eap_method_index--; + } else if (sm->method_pending == METHOD_PENDING_CONT) { + wpa_printf(MSG_DEBUG, "EAP: Method has completed " + "pending processing - reprocess pending " + "EAP message"); + sm->method_pending = METHOD_PENDING_NONE; + SM_ENTER(EAP, PROPOSE_METHOD); + } else + SM_ENTER(EAP, METHOD_REQUEST); + break; + case EAP_NAK: + SM_ENTER(EAP, SELECT_ACTION); + break; + case EAP_SELECT_ACTION: + if (sm->decision == DECISION_FAILURE) + SM_ENTER(EAP, FAILURE); + else if (sm->decision == DECISION_SUCCESS) + SM_ENTER(EAP, SUCCESS); +#ifndef ESP_SUPPLICANT + else if (sm->decision == DECISION_PASSTHROUGH) + SM_ENTER(EAP, INITIALIZE_PASSTHROUGH); +#endif + else if (sm->decision == DECISION_INITIATE_REAUTH_START) + SM_ENTER(EAP, INITIATE_REAUTH_START); +#ifdef CONFIG_ERP + else if (sm->cfg->eap_server && sm->cfg->erp && sm->rxInitiate) + SM_ENTER(EAP, INITIATE_RECEIVED); +#endif /* CONFIG_ERP */ + else + SM_ENTER(EAP, PROPOSE_METHOD); + break; + case EAP_INITIATE_REAUTH_START: + SM_ENTER(EAP, SEND_REQUEST); + break; + case EAP_INITIATE_RECEIVED: + if (!sm->cfg->eap_server) + SM_ENTER(EAP, SELECT_ACTION); + break; + case EAP_TIMEOUT_FAILURE: + break; + case EAP_FAILURE: + break; + case EAP_SUCCESS: + break; +#ifndef ESP_SUPPLICANT + case EAP_INITIALIZE_PASSTHROUGH: + if (sm->currentId == -1) + SM_ENTER(EAP, AAA_IDLE); + else + SM_ENTER(EAP, AAA_REQUEST); + break; + case EAP_IDLE2: + if (sm->eap_if.eapResp) + SM_ENTER(EAP, RECEIVED2); + else if (sm->eap_if.retransWhile == 0) + SM_ENTER(EAP, RETRANSMIT2); + break; + case EAP_RETRANSMIT2: + if (sm->retransCount > sm->MaxRetrans) + SM_ENTER(EAP, TIMEOUT_FAILURE2); + else + SM_ENTER(EAP, IDLE2); + break; + case EAP_RECEIVED2: + if (sm->rxResp && (sm->respId == sm->currentId)) + SM_ENTER(EAP, AAA_REQUEST); + else + SM_ENTER(EAP, DISCARD2); + break; + case EAP_DISCARD2: + SM_ENTER(EAP, IDLE2); + break; + case EAP_SEND_REQUEST2: + SM_ENTER(EAP, IDLE2); + break; + case EAP_AAA_REQUEST: + SM_ENTER(EAP, AAA_IDLE); + break; + case EAP_AAA_RESPONSE: + SM_ENTER(EAP, SEND_REQUEST2); + break; + case EAP_AAA_IDLE: + if (sm->eap_if.aaaFail) + SM_ENTER(EAP, FAILURE2); + else if (sm->eap_if.aaaSuccess) + SM_ENTER(EAP, SUCCESS2); + else if (sm->eap_if.aaaEapReq) + SM_ENTER(EAP, AAA_RESPONSE); + else if (sm->eap_if.aaaTimeout) + SM_ENTER(EAP, TIMEOUT_FAILURE2); + break; + case EAP_TIMEOUT_FAILURE2: + break; + case EAP_FAILURE2: + break; + case EAP_SUCCESS2: + break; +#else + default: + break; +#endif + } +} + + +static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, + int eapSRTT, int eapRTTVAR, + int methodTimeout) +{ + int rto, i; + + if (sm->try_initiate_reauth) { + wpa_printf(MSG_DEBUG, + "EAP: retransmit timeout 1 second for EAP-Initiate-Re-auth-Start"); + return 1; + } + + if (methodTimeout) { + /* + * EAP method (either internal or through AAA server, provided + * timeout hint. Use that as-is as a timeout for retransmitting + * the EAP request if no response is received. + */ + wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds " + "(from EAP method hint)", methodTimeout); + return methodTimeout; + } + + /* + * RFC 3748 recommends algorithms described in RFC 2988 for estimation + * of the retransmission timeout. This should be implemented once + * round-trip time measurements are available. For nowm a simple + * backoff mechanism is used instead if there are no EAP method + * specific hints. + * + * SRTT = smoothed round-trip time + * RTTVAR = round-trip time variation + * RTO = retransmission timeout + */ + + /* + * RFC 2988, 2.1: before RTT measurement, set RTO to 3 seconds for + * initial retransmission and then double the RTO to provide back off + * per 5.5. Limit the maximum RTO to 20 seconds per RFC 3748, 4.3 + * modified RTOmax. + */ + rto = 3; + for (i = 0; i < retransCount; i++) { + rto *= 2; + if (rto >= 20) { + rto = 20; + break; + } + } + + wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds " + "(from dynamic back off; retransCount=%d)", + rto, retransCount); + + return rto; +} + + +static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp) +{ + const struct eap_hdr *hdr; + size_t plen; + + /* parse rxResp, respId, respMethod */ + sm->rxResp = false; + sm->rxInitiate = false; + sm->respId = -1; + sm->respMethod = EAP_TYPE_NONE; + sm->respVendor = EAP_VENDOR_IETF; + sm->respVendorMethod = EAP_TYPE_NONE; + + if (resp == NULL || wpabuf_len(resp) < sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, "EAP: parseEapResp: invalid resp=%p " + "len=%lu", resp, + resp ? (unsigned long) wpabuf_len(resp) : 0); + return; + } + + hdr = wpabuf_head(resp); + plen = be_to_host16(hdr->length); + if (plen > wpabuf_len(resp)) { + wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet " + "(len=%lu plen=%lu)", + (unsigned long) wpabuf_len(resp), + (unsigned long) plen); + return; + } + + sm->respId = hdr->identifier; + + if (hdr->code == EAP_CODE_RESPONSE) + sm->rxResp = true; + else if (hdr->code == EAP_CODE_INITIATE) + sm->rxInitiate = true; + + if (plen > sizeof(*hdr)) { + u8 *pos = (u8 *) (hdr + 1); + sm->respMethod = *pos++; + if (sm->respMethod == EAP_TYPE_EXPANDED) { + if (plen < sizeof(*hdr) + 8) { + wpa_printf(MSG_DEBUG, "EAP: Ignored truncated " + "expanded EAP-Packet (plen=%lu)", + (unsigned long) plen); + return; + } + sm->respVendor = WPA_GET_BE24(pos); + pos += 3; + sm->respVendorMethod = WPA_GET_BE32(pos); + } + } + + wpa_printf(MSG_DEBUG, + "EAP: parseEapResp: rxResp=%d rxInitiate=%d respId=%d respMethod=%u respVendor=%u respVendorMethod=%u", + sm->rxResp, sm->rxInitiate, sm->respId, sm->respMethod, + sm->respVendor, sm->respVendorMethod); +} + + +#ifndef ESP_SUPPLICANT +static int eap_sm_getId(const struct wpabuf *data) +{ + const struct eap_hdr *hdr; + + if (data == NULL || wpabuf_len(data) < sizeof(*hdr)) + return -1; + + hdr = wpabuf_head(data); + wpa_printf(MSG_DEBUG, "EAP: getId: id=%d", hdr->identifier); + return hdr->identifier; +} +#endif + +static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id) +{ + struct wpabuf *msg; + struct eap_hdr *resp; + wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id); + + msg = wpabuf_alloc(sizeof(*resp)); + if (msg == NULL) + return NULL; + resp = wpabuf_put(msg, sizeof(*resp)); + resp->code = EAP_CODE_SUCCESS; + resp->identifier = id; + resp->length = host_to_be16(sizeof(*resp)); + + return msg; +} + + +static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id) +{ + struct wpabuf *msg; + struct eap_hdr *resp; + wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id); + + msg = wpabuf_alloc(sizeof(*resp)); + if (msg == NULL) + return NULL; + resp = wpabuf_put(msg, sizeof(*resp)); + resp->code = EAP_CODE_FAILURE; + resp->identifier = id; + resp->length = host_to_be16(sizeof(*resp)); + + return msg; +} + + +static int eap_sm_nextId(struct eap_sm *sm, int id) +{ + if (id < 0) { + /* RFC 3748 Ch 4.1: recommended to initialize Identifier with a + * random number */ + id = rand() & 0xff; + if (id != sm->lastId) + return id; + } + return (id + 1) & 0xff; +} + + +/** + * eap_sm_process_nak - Process EAP-Response/Nak + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * @nak_list: Nak list (allowed methods) from the supplicant + * @len: Length of nak_list in bytes + * + * This function is called when EAP-Response/Nak is received from the + * supplicant. This can happen for both phase 1 and phase 2 authentications. + */ +void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len) +{ + int i; + size_t j; + + if (sm->user == NULL) + return; + + wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method " + "index %d)", sm->user_eap_method_index); + + wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods", + (u8 *) sm->user->methods, + EAP_MAX_METHODS * sizeof(sm->user->methods[0])); + wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer", + nak_list, len); + + i = sm->user_eap_method_index; + while (i < EAP_MAX_METHODS && + (sm->user->methods[i].vendor != EAP_VENDOR_IETF || + sm->user->methods[i].method != EAP_TYPE_NONE)) { + if (sm->user->methods[i].vendor != EAP_VENDOR_IETF) + goto not_found; + for (j = 0; j < len; j++) { + if (nak_list[j] == sm->user->methods[i].method) { + break; + } + } + + if (j < len) { + /* found */ + i++; + continue; + } + + not_found: + /* not found - remove from the list */ + if (i + 1 < EAP_MAX_METHODS) { + os_memmove(&sm->user->methods[i], + &sm->user->methods[i + 1], + (EAP_MAX_METHODS - i - 1) * + sizeof(sm->user->methods[0])); + } + sm->user->methods[EAP_MAX_METHODS - 1].vendor = + EAP_VENDOR_IETF; + sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE; + } + + wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods", + (u8 *) sm->user->methods, EAP_MAX_METHODS * + sizeof(sm->user->methods[0])); +} + + +static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list, + size_t len) +{ + if (nak_list == NULL || sm == NULL || sm->user == NULL) + return; + + if (sm->user->phase2) { + wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user" + " info was selected - reject"); + sm->decision = DECISION_FAILURE; + return; + } + + eap_sm_process_nak(sm, nak_list, len); +} + + +static enum eap_type eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor) +{ + enum eap_type next; + int idx = sm->user_eap_method_index; + + /* In theory, there should be no problems with starting + * re-authentication with something else than EAP-Request/Identity and + * this does indeed work with wpa_supplicant. However, at least Funk + * Supplicant seemed to ignore re-auth if it skipped + * EAP-Request/Identity. + * Re-auth sets currentId == -1, so that can be used here to select + * whether Identity needs to be requested again. */ + if (sm->identity == NULL || sm->currentId == -1) { + *vendor = EAP_VENDOR_IETF; + next = EAP_TYPE_IDENTITY; + sm->update_user = true; + } else if (sm->user && idx < EAP_MAX_METHODS && + (sm->user->methods[idx].vendor != EAP_VENDOR_IETF || + sm->user->methods[idx].method != EAP_TYPE_NONE)) { + *vendor = sm->user->methods[idx].vendor; + next = sm->user->methods[idx].method; + sm->user_eap_method_index++; + } else { + *vendor = EAP_VENDOR_IETF; + next = EAP_TYPE_NONE; + } + wpa_printf(MSG_DEBUG, "EAP: getNextMethod: vendor %d type %d", + *vendor, next); + return next; +} + + +static int eap_sm_Policy_getDecision(struct eap_sm *sm) +{ + if (!sm->cfg->eap_server && sm->identity && !sm->start_reauth) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: -> PASSTHROUGH"); + return DECISION_PASSTHROUGH; + } + + if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY && + sm->m->isSuccess(sm, sm->eap_method_priv)) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> " + "SUCCESS"); + sm->update_user = true; + return DECISION_SUCCESS; + } + + if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) && + !sm->m->isSuccess(sm, sm->eap_method_priv)) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> " + "FAILURE"); + sm->update_user = true; + return DECISION_FAILURE; + } + + if ((sm->user == NULL || sm->update_user) && sm->identity && + !sm->start_reauth) { + /* + * Allow Identity method to be started once to allow identity + * selection hint to be sent from the authentication server, + * but prevent a loop of Identity requests by only allowing + * this to happen once. + */ + int id_req = 0; + if (sm->user && sm->currentMethod == EAP_TYPE_IDENTITY && + sm->user->methods[0].vendor == EAP_VENDOR_IETF && + sm->user->methods[0].method == EAP_TYPE_IDENTITY) + id_req = 1; + if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: user not " + "found from database -> FAILURE"); + return DECISION_FAILURE; + } + if (id_req && sm->user && + sm->user->methods[0].vendor == EAP_VENDOR_IETF && + sm->user->methods[0].method == EAP_TYPE_IDENTITY) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: stop " + "identity request loop -> FAILURE"); + sm->update_user = true; + return DECISION_FAILURE; + } + sm->update_user = false; + } + sm->start_reauth = false; + + if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && + (sm->user->methods[sm->user_eap_method_index].vendor != + EAP_VENDOR_IETF || + sm->user->methods[sm->user_eap_method_index].method != + EAP_TYPE_NONE)) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: another method " + "available -> CONTINUE"); + return DECISION_CONTINUE; + } + + if (!sm->identity && eap_get_erp_send_reauth_start(sm) && + !sm->initiate_reauth_start_sent) { + wpa_printf(MSG_DEBUG, + "EAP: getDecision: send EAP-Initiate/Re-auth-Start"); + return DECISION_INITIATE_REAUTH_START; + } + + if (sm->identity == NULL || sm->currentId == -1) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known " + "yet -> CONTINUE"); + return DECISION_CONTINUE; + } + + wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> " + "FAILURE"); + return DECISION_FAILURE; +} + + +static bool eap_sm_Policy_doPickUp(struct eap_sm *sm, enum eap_type method) +{ + return method == EAP_TYPE_IDENTITY; +} + + +/** + * eap_server_sm_step - Step EAP server state machine + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: 1 if EAP state was changed or 0 if not + * + * This function advances EAP state machine to a new state to match with the + * current variables. This should be called whenever variables used by the EAP + * state machine have changed. + */ +int eap_server_sm_step(struct eap_sm *sm) +{ + int res = 0; + do { + sm->changed = false; + SM_STEP_RUN(EAP); + if (sm->changed) + res = 1; + } while (sm->changed); + return res; +} + + +void eap_user_free(struct eap_user *user) +{ + if (user == NULL) + return; + bin_clear_free(user->password, user->password_len); + user->password = NULL; + bin_clear_free(user->salt, user->salt_len); + user->salt = NULL; + os_free(user); +} + + +/** + * eap_server_sm_init - Allocate and initialize EAP server state machine + * @eapol_ctx: Context data to be used with eapol_cb calls + * @eapol_cb: Pointer to EAPOL callback functions + * @conf: EAP configuration + * Returns: Pointer to the allocated EAP state machine or %NULL on failure + * + * This function allocates and initializes an EAP state machine. + */ +struct eap_sm * eap_server_sm_init(void *eapol_ctx, + const struct eapol_callbacks *eapol_cb, + const struct eap_config *conf, + const struct eap_session_data *sess) +{ + struct eap_sm *sm; + + sm = os_zalloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + sm->eapol_ctx = eapol_ctx; + sm->eapol_cb = eapol_cb; + sm->MaxRetrans = 5; /* RFC 3748: max 3-5 retransmissions suggested */ + sm->cfg = conf; + if (sess->assoc_wps_ie) + sm->assoc_wps_ie = wpabuf_dup(sess->assoc_wps_ie); + if (sess->assoc_p2p_ie) + sm->assoc_p2p_ie = wpabuf_dup(sess->assoc_p2p_ie); + if (sess->peer_addr) + os_memcpy(sm->peer_addr, sess->peer_addr, ETH_ALEN); +#ifdef CONFIG_TESTING_OPTIONS + sm->tls_test_flags = sess->tls_test_flags; +#endif /* CONFIG_TESTING_OPTIONS */ + sm->eap_if.portEnabled = 1; + + wpa_printf(MSG_DEBUG, "EAP: Server state machine created"); + + return sm; +} + + +/** + * eap_server_sm_deinit - Deinitialize and free an EAP server state machine + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * + * This function deinitializes EAP state machine and frees all allocated + * resources. + */ +void eap_server_sm_deinit(struct eap_sm *sm) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAP: Server state machine removed"); + if (sm->m && sm->eap_method_priv) + sm->m->reset(sm, sm->eap_method_priv); + wpabuf_free(sm->eap_if.eapReqData); + bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); + os_free(sm->eap_if.eapSessionId); + wpabuf_free(sm->lastReqData); + wpabuf_free(sm->eap_if.eapRespData); + os_free(sm->identity); + os_free(sm->serial_num); +#ifndef ESP_SUPPLICANT + wpabuf_free(sm->eap_if.aaaEapReqData); + wpabuf_free(sm->eap_if.aaaEapRespData); + bin_clear_free(sm->eap_if.aaaEapKeyData, sm->eap_if.aaaEapKeyDataLen); +#endif + eap_user_free(sm->user); + wpabuf_free(sm->assoc_wps_ie); + wpabuf_free(sm->assoc_p2p_ie); + os_free(sm); +} + + +/** + * eap_sm_notify_cached - Notify EAP state machine of cached PMK + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * + * This function is called when PMKSA caching is used to skip EAP + * authentication. + */ +void eap_sm_notify_cached(struct eap_sm *sm) +{ + if (sm == NULL) + return; + + sm->EAP_state = EAP_SUCCESS; +} + + +/** + * eap_sm_pending_cb - EAP state machine callback for a pending EAP request + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * + * This function is called when data for a pending EAP-Request is received. + */ +void eap_sm_pending_cb(struct eap_sm *sm) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAP: Callback for pending request received"); + if (sm->method_pending == METHOD_PENDING_WAIT) + sm->method_pending = METHOD_PENDING_CONT; +} + + +/** + * eap_sm_method_pending - Query whether EAP method is waiting for pending data + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: 1 if method is waiting for pending data or 0 if not + */ +int eap_sm_method_pending(struct eap_sm *sm) +{ + if (sm == NULL) + return 0; + return sm->method_pending == METHOD_PENDING_WAIT; +} + + +/** + * eap_get_identity - Get the user identity (from EAP-Response/Identity) + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * @len: Buffer for returning identity length + * Returns: Pointer to the user identity or %NULL if not available + */ +const u8 * eap_get_identity(struct eap_sm *sm, size_t *len) +{ + *len = sm->identity_len; + return sm->identity; +} + + +/** + * eap_get_serial_num - Get the serial number of user certificate + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: Pointer to the serial number or %NULL if not available + */ +const char * eap_get_serial_num(struct eap_sm *sm) +{ + return sm->serial_num; +} + + +/** + * eap_get_method - Get the used EAP method + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: Pointer to the method name or %NULL if not available + */ +const char * eap_get_method(struct eap_sm *sm) +{ + if (!sm || !sm->m) + return NULL; + return sm->m->name; +} + + +/** + * eap_get_imsi - Get IMSI of the user + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: Pointer to IMSI or %NULL if not available + */ +const char * eap_get_imsi(struct eap_sm *sm) +{ + if (!sm || sm->imsi[0] == '\0') + return NULL; + return sm->imsi; +} + + +void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len) +{ +#ifdef CONFIG_ERP + const struct eap_hdr *hdr; + const u8 *pos, *end; + struct erp_tlvs parse; + + if (len < sizeof(*hdr) + 1) + return; + hdr = (const struct eap_hdr *) eap; + end = eap + len; + pos = (const u8 *) (hdr + 1); + if (hdr->code != EAP_CODE_INITIATE || *pos != EAP_ERP_TYPE_REAUTH) + return; + pos++; + if (pos + 3 > end) + return; + + /* Skip Flags and SEQ */ + pos += 3; + + if (erp_parse_tlvs(pos, end, &parse, 1) < 0 || !parse.keyname) + return; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP: Update identity based on EAP-Initiate/Re-auth keyName-NAI", + parse.keyname, parse.keyname_len); + os_free(sm->identity); + sm->identity = os_malloc(parse.keyname_len); + if (sm->identity) { + os_memcpy(sm->identity, parse.keyname, parse.keyname_len); + sm->identity_len = parse.keyname_len; + } else { + sm->identity_len = 0; + } +#endif /* CONFIG_ERP */ +} + + +/** + * eap_get_interface - Get pointer to EAP-EAPOL interface data + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: Pointer to the EAP-EAPOL interface data + */ +struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm) +{ + return &sm->eap_if; +} + + +/** + * eap_server_clear_identity - Clear EAP identity information + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * + * This function can be used to clear the EAP identity information in the EAP + * server context. This allows the EAP/Identity method to be used again after + * EAPOL-Start or EAPOL-Logoff. + */ +void eap_server_clear_identity(struct eap_sm *sm) +{ + os_free(sm->identity); + sm->identity = NULL; +} + + +#ifdef CONFIG_TESTING_OPTIONS +void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source, + const u8 *username, size_t username_len, + const u8 *challenge, const u8 *response) +{ + char hex_challenge[30], hex_response[90], user[100]; + + /* Print out Challenge and Response in format supported by asleap. */ + if (username) + printf_encode(user, sizeof(user), username, username_len); + else + user[0] = '\0'; + wpa_snprintf_hex_sep(hex_challenge, sizeof(hex_challenge), + challenge, sizeof(challenge), ':'); + wpa_snprintf_hex_sep(hex_response, sizeof(hex_response), response, 24, + ':'); + wpa_printf(MSG_DEBUG, "[%s/user=%s] asleap -C %s -R %s", + source, user, hex_challenge, hex_response); +} +#endif /* CONFIG_TESTING_OPTIONS */ + + +void eap_server_config_free(struct eap_config *cfg) +{ + if (!cfg) + return; + os_free(cfg->pac_opaque_encr_key); + os_free(cfg->eap_fast_a_id); + os_free(cfg->eap_fast_a_id_info); + os_free(cfg->server_id); + os_free(cfg); +} diff --git a/components/wpa_supplicant/src/eap_server/eap_server_identity.c b/components/wpa_supplicant/src/eap_server/eap_server_identity.c new file mode 100644 index 0000000000..0f707c482c --- /dev/null +++ b/components/wpa_supplicant/src/eap_server/eap_server_identity.c @@ -0,0 +1,177 @@ +/* + * hostapd / EAP-Identity + * Copyright (c) 2004-2006, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" + + +struct eap_identity_data { + enum { CONTINUE, SUCCESS, FAILURE } state; + int pick_up; +}; + + +static void * eap_identity_init(struct eap_sm *sm) +{ + struct eap_identity_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = CONTINUE; + + return data; +} + + +static void * eap_identity_initPickUp(struct eap_sm *sm) +{ + struct eap_identity_data *data; + data = eap_identity_init(sm); + if (data) { + data->pick_up = 1; + } + return data; +} + + +static void eap_identity_reset(struct eap_sm *sm, void *priv) +{ + struct eap_identity_data *data = priv; + os_free(data); +} + + +static struct wpabuf * eap_identity_buildReq(struct eap_sm *sm, void *priv, + u8 id) +{ + struct eap_identity_data *data = priv; + struct wpabuf *req; + const char *req_data; + size_t req_data_len; + + if (sm->eapol_cb->get_eap_req_id_text) { + req_data = sm->eapol_cb->get_eap_req_id_text(sm->eapol_ctx, + &req_data_len); + } else { + req_data = NULL; + req_data_len = 0; + } + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req_data_len, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-Identity: Failed to allocate " + "memory for request"); + data->state = FAILURE; + return NULL; + } + + wpabuf_put_data(req, req_data, req_data_len); + + return req; +} + + +static bool eap_identity_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, + respData, &len); + if (pos == NULL) { + wpa_printf(MSG_INFO, "EAP-Identity: Invalid frame"); + return true; + } + + return false; +} + + +static void eap_identity_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_identity_data *data = priv; + const u8 *pos; + size_t len; + char *buf; + + if (data->pick_up) { + if (eap_identity_check(sm, data, respData)) { + wpa_printf(MSG_DEBUG, "EAP-Identity: failed to pick " + "up already started negotiation"); + data->state = FAILURE; + return; + } + data->pick_up = 0; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, + respData, &len); + if (pos == NULL) + return; /* Should not happen - frame already validated */ + + wpa_hexdump_ascii(MSG_DEBUG, "EAP-Identity: Peer identity", pos, len); + buf = os_malloc(len * 4 + 1); + if (buf) { + printf_encode(buf, len * 4 + 1, pos, len); + wpa_printf(MSG_DEBUG, "EAP-Response/Identity '%s'", buf); + os_free(buf); + } + if (sm->identity) + sm->update_user = true; + os_free(sm->identity); + sm->identity = os_malloc(len ? len : 1); + if (sm->identity == NULL) { + data->state = FAILURE; + } else { + os_memcpy(sm->identity, pos, len); + sm->identity_len = len; + data->state = SUCCESS; + } +} + + +static bool eap_identity_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_identity_data *data = priv; + return data->state != CONTINUE; +} + + +static bool eap_identity_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_identity_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_identity_register(void) +{ + struct eap_method *eap; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, + "Identity"); + if (eap == NULL) + return -1; + + eap->init = eap_identity_init; + eap->initPickUp = eap_identity_initPickUp; + eap->reset = eap_identity_reset; + eap->buildReq = eap_identity_buildReq; + eap->check = eap_identity_check; + eap->process = eap_identity_process; + eap->isDone = eap_identity_isDone; + eap->isSuccess = eap_identity_isSuccess; + + return eap_server_method_register(eap); +} diff --git a/components/wpa_supplicant/src/eap_server/eap_server_methods.c b/components/wpa_supplicant/src/eap_server/eap_server_methods.c new file mode 100644 index 0000000000..f37c9c3835 --- /dev/null +++ b/components/wpa_supplicant/src/eap_server/eap_server_methods.c @@ -0,0 +1,178 @@ +/* + * EAP server method registration + * Copyright (c) 2004-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" +#include "eap_methods.h" + + +static struct eap_method *eap_methods; + + +/** + * eap_server_get_eap_method - Get EAP method based on type number + * @vendor: EAP Vendor-Id (0 = IETF) + * @method: EAP type number + * Returns: Pointer to EAP method or %NULL if not found + */ +const struct eap_method * eap_server_get_eap_method(int vendor, + enum eap_type method) +{ + struct eap_method *m; + for (m = eap_methods; m; m = m->next) { + if (m->vendor == vendor && m->method == method) + return m; + } + return NULL; +} + + +/** + * eap_server_get_type - Get EAP type for the given EAP method name + * @name: EAP method name, e.g., TLS + * @vendor: Buffer for returning EAP Vendor-Id + * Returns: EAP method type or %EAP_TYPE_NONE if not found + * + * This function maps EAP type names into EAP type numbers based on the list of + * EAP methods included in the build. + */ +enum eap_type eap_server_get_type(const char *name, int *vendor) +{ + struct eap_method *m; + for (m = eap_methods; m; m = m->next) { + if (os_strcmp(m->name, name) == 0) { + *vendor = m->vendor; + return m->method; + } + } + *vendor = EAP_VENDOR_IETF; + return EAP_TYPE_NONE; +} + + +/** + * eap_server_method_alloc - Allocate EAP server method structure + * @version: Version of the EAP server method interface (set to + * EAP_SERVER_METHOD_INTERFACE_VERSION) + * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF) + * @method: EAP type number (EAP_TYPE_*) + * @name: Name of the method (e.g., "TLS") + * Returns: Allocated EAP method structure or %NULL on failure + * + * The returned structure should be freed with eap_server_method_free() when it + * is not needed anymore. + */ +struct eap_method * eap_server_method_alloc(int version, int vendor, + enum eap_type method, + const char *name) +{ + struct eap_method *eap; + eap = os_zalloc(sizeof(*eap)); + if (eap == NULL) + return NULL; + eap->version = version; + eap->vendor = vendor; + eap->method = method; + eap->name = name; + return eap; +} + + +/** + * eap_server_method_free - Free EAP server method structure + * @method: Method structure allocated with eap_server_method_alloc() + */ +static void eap_server_method_free(struct eap_method *method) +{ + os_free(method); +} + + +/** + * eap_server_method_register - Register an EAP server method + * @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. 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) { + 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) { + eap_server_method_free(method); + return -2; + } + last = m; + } + + if (last) + last->next = method; + else + eap_methods = method; + + return 0; +} + + +/** + * eap_server_unregister_methods - Unregister EAP server methods + * + * This function is called at program termination to unregister all EAP server + * methods. + */ +void eap_server_unregister_methods(void) +{ + struct eap_method *m; + + while (eap_methods) { + m = eap_methods; + eap_methods = eap_methods->next; + + if (m->free) + m->free(m); + else + eap_server_method_free(m); + } +} + + +/** + * eap_server_get_name - Get EAP method name for the given EAP type + * @vendor: EAP Vendor-Id (0 = IETF) + * @type: EAP method type + * Returns: EAP method name, e.g., TLS, or "unknown" if not found + * + * This function maps EAP type numbers into EAP type names based on the list of + * EAP methods included in the build. + */ +const char * eap_server_get_name(int vendor, enum eap_type type) +{ + struct eap_method *m; + if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED) + return "expanded"; + for (m = eap_methods; m; m = m->next) { + if (m->vendor == vendor && m->method == type) + return m->name; + } + return "unknown"; +} diff --git a/components/wpa_supplicant/src/eap_server/eap_server_wsc.c b/components/wpa_supplicant/src/eap_server/eap_server_wsc.c new file mode 100644 index 0000000000..861d97ab9c --- /dev/null +++ b/components/wpa_supplicant/src/eap_server/eap_server_wsc.c @@ -0,0 +1,450 @@ +/* + * EAP-WSC server for Wi-Fi Protected Setup + * Copyright (c) 2007-2008, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "eap_i.h" +#include "eap_common/eap_wsc_common.h" +#include "wps/wps.h" +#include "esp_wps_i.h" + + +struct eap_wsc_data { + enum { START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, WSC_FAIL } state; + int registrar; + struct wpabuf *in_buf; + struct wpabuf *out_buf; + enum wsc_op_code in_op_code, out_op_code; + size_t out_used; + size_t fragment_size; + struct wps_data *wps; + int ext_reg_timeout; +}; + +#ifdef DEBUG_PRINT +static const char * eap_wsc_state_txt(int state) +{ + switch (state) { + case START: + return "START"; + case MESG: + return "MESG"; + case FRAG_ACK: + return "FRAG_ACK"; + case WAIT_FRAG_ACK: + return "WAIT_FRAG_ACK"; + case DONE: + return "DONE"; + case WSC_FAIL: + return "FAIL"; + default: + return "?"; + } +} +#endif + +static void eap_wsc_state(struct eap_wsc_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s", + eap_wsc_state_txt(data->state), + eap_wsc_state_txt(state)); + data->state = state; +} + + +static void * eap_wsc_init(struct eap_sm *sm) +{ + struct eap_wsc_data *data; + int registrar; + + if (sm->identity && sm->identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(sm->identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == + 0) + registrar = 0; /* Supplicant is Registrar */ + else if (sm->identity && sm->identity_len == WSC_ID_ENROLLEE_LEN && + os_memcmp(sm->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) + == 0) + registrar = 1; /* Supplicant is Enrollee */ + else { + wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity", + sm->identity, sm->identity_len); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = registrar ? START : MESG; + data->registrar = registrar; + + struct wps_sm *wps_sm = wps_sm_get(); + data->wps = wps_sm->wps; + if (data->wps == NULL) { + os_free(data); + return NULL; + } + data->fragment_size = WSC_FRAGMENT_SIZE; + + return data; +} + + +static void eap_wsc_reset(struct eap_sm *sm, void *priv) +{ + struct eap_wsc_data *data = priv; + wpabuf_free(data->in_buf); + wpabuf_free(data->out_buf); + wps_deinit(data->wps); + os_free(data); +} + + +static struct wpabuf * eap_wsc_build_start(struct eap_sm *sm, + struct eap_wsc_data *data, u8 id) +{ + struct wpabuf *req; + + req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for " + "request"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/Start"); + wpabuf_put_u8(req, WSC_Start); /* Op-Code */ + wpabuf_put_u8(req, 0); /* Flags */ + + return req; +} + + +static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, u8 id) +{ + struct wpabuf *req; + u8 flags; + size_t send_len, plen; + + flags = 0; + send_len = wpabuf_len(data->out_buf) - data->out_used; + if (2 + send_len > data->fragment_size) { + send_len = data->fragment_size - 2; + flags |= WSC_FLAGS_MF; + if (data->out_used == 0) { + flags |= WSC_FLAGS_LF; + send_len -= 2; + } + } + plen = 2 + send_len; + if (flags & WSC_FLAGS_LF) + plen += 2; + req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for " + "request"); + return NULL; + } + + wpabuf_put_u8(req, data->out_op_code); /* Op-Code */ + wpabuf_put_u8(req, flags); /* Flags */ + if (flags & WSC_FLAGS_LF) + wpabuf_put_be16(req, wpabuf_len(data->out_buf)); + + wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, + send_len); + data->out_used += send_len; + + if (data->out_used == wpabuf_len(data->out_buf)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->out_buf); + data->out_buf = NULL; + data->out_used = 0; + eap_wsc_state(data, MESG); + } else { + wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->out_buf) - + data->out_used); + eap_wsc_state(data, WAIT_FRAG_ACK); + } + + return req; +} + + +static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_wsc_data *data = priv; + + switch (data->state) { + case START: + return eap_wsc_build_start(sm, data, id); + case MESG: + if (data->out_buf == NULL) { + data->out_buf = wps_get_msg(data->wps, + &data->out_op_code); + if (data->out_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to " + "receive message from WPS"); + return NULL; + } + data->out_used = 0; + } + /* fall through */ + case WAIT_FRAG_ACK: + return eap_wsc_build_msg(data, id); + case FRAG_ACK: + return eap_wsc_build_frag_ack(id, EAP_CODE_REQUEST); + default: + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected state %d in " + "buildReq", data->state); + return NULL; + } +} + + +static bool eap_wsc_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, + respData, &len); + if (pos == NULL || len < 2) { + wpa_printf(MSG_INFO, "EAP-WSC: Invalid frame"); + return true; + } + + return false; +} + + +static int eap_wsc_process_cont(struct eap_wsc_data *data, + const u8 *buf, size_t len, u8 op_code) +{ + /* Process continuation of a pending message */ + if (op_code != data->in_op_code) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in " + "fragment (expected %d)", + op_code, data->in_op_code); + eap_wsc_state(data, WSC_FAIL); + return -1; + } + + if (len > wpabuf_tailroom(data->in_buf)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow"); + eap_wsc_state(data, WSC_FAIL); + return -1; + } + + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting for %lu " + "bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + + return 0; +} + + +static int eap_wsc_process_fragment(struct eap_wsc_data *data, + u8 flags, u8 op_code, u16 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length " + "field in a fragmented packet"); + return -1; + } + + if (data->in_buf == NULL) { + /* First fragment of the message */ + data->in_buf = wpabuf_alloc(message_length); + if (data->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for " + "message"); + return -1; + } + data->in_op_code = op_code; + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in " + "first fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + } + + return 0; +} + + +static void eap_wsc_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_wsc_data *data = priv; + const u8 *start, *pos, *end; + size_t len; + u8 op_code, flags; + u16 message_length = 0; + enum wps_process_res res; + struct wpabuf tmpbuf; + + if (data->ext_reg_timeout) { + eap_wsc_state(data, WSC_FAIL); + return; + } + + pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, + respData, &len); + if (pos == NULL || len < 2) + return; /* Should not happen; message already verified */ + + start = pos; + end = start + len; + + op_code = *pos++; + flags = *pos++; + if (flags & WSC_FLAGS_LF) { + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow"); + return; + } + message_length = WPA_GET_BE16(pos); + pos += 2; + + if (message_length < end - pos || message_length > 50000) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " + "Length"); + return; + } + } + + wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d " + "Flags 0x%x Message Length %d", + op_code, flags, message_length); + + if (data->state == WAIT_FRAG_ACK) { + if (op_code != WSC_FRAG_ACK) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " + "in WAIT_FRAG_ACK state", op_code); + eap_wsc_state(data, WSC_FAIL); + return; + } + wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged"); + eap_wsc_state(data, MESG); + return; + } + + if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG && + op_code != WSC_Done) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", + op_code); + eap_wsc_state(data, WSC_FAIL); + return; + } + + if (data->in_buf && + eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) { + eap_wsc_state(data, WSC_FAIL); + return; + } + + if (flags & WSC_FLAGS_MF) { + if (eap_wsc_process_fragment(data, flags, op_code, + message_length, pos, end - pos) < + 0) + eap_wsc_state(data, WSC_FAIL); + else + eap_wsc_state(data, FRAG_ACK); + return; + } + + if (data->in_buf == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&tmpbuf, pos, end - pos); + data->in_buf = &tmpbuf; + } + + res = wps_process_msg(data->wps, op_code, data->in_buf); + switch (res) { + case WPS_DONE: + wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed " + "successfully - report EAP failure"); + eap_wsc_state(data, WSC_FAIL); + break; + case WPS_CONTINUE: + eap_wsc_state(data, MESG); + break; + case WPS_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed"); + eap_wsc_state(data, WSC_FAIL); + break; + case WPS_PENDING: + eap_wsc_state(data, MESG); + sm->method_pending = METHOD_PENDING_WAIT; + break; + default: + break; + } + + if (data->in_buf != &tmpbuf) + wpabuf_free(data->in_buf); + data->in_buf = NULL; +} + + +static bool eap_wsc_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_wsc_data *data = priv; + return data->state == WSC_FAIL; +} + + +static bool eap_wsc_isSuccess(struct eap_sm *sm, void *priv) +{ + /* EAP-WSC will always result in EAP-Failure */ + return false; +} + + +static int eap_wsc_getTimeout(struct eap_sm *sm, void *priv) +{ + /* Recommended retransmit times: retransmit timeout 5 seconds, + * per-message timeout 15 seconds, i.e., 3 tries. */ + sm->MaxRetrans = 2; /* total 3 attempts */ + return 5; +} + + +int eap_server_wsc_register(void) +{ + struct eap_method *eap; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, + "WSC"); + if (eap == NULL) + return -1; + + eap->init = eap_wsc_init; + eap->reset = eap_wsc_reset; + eap->buildReq = eap_wsc_buildReq; + eap->check = eap_wsc_check; + eap->process = eap_wsc_process; + eap->isDone = eap_wsc_isDone; + eap->isSuccess = eap_wsc_isSuccess; + eap->getTimeout = eap_wsc_getTimeout; + + return eap_server_method_register(eap); +} diff --git a/components/wpa_supplicant/src/eapol_auth/eapol_auth_sm.c b/components/wpa_supplicant/src/eapol_auth/eapol_auth_sm.c new file mode 100644 index 0000000000..c77f40b00e --- /dev/null +++ b/components/wpa_supplicant/src/eapol_auth/eapol_auth_sm.c @@ -0,0 +1,1266 @@ +/* + * IEEE 802.1X-2004 Authenticator - EAPOL state machine + * Copyright (c) 2002-2015, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "state_machine.h" +#include "common/eapol_common.h" +#include "eap_peer/eap_defs.h" +#include "eap_peer/eap_common.h" +#include "eap_server/eap.h" +#include "eapol_auth_sm.h" +#include "eapol_auth_sm_i.h" + +#define STATE_MACHINE_DATA struct eapol_state_machine +#define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X" +#define STATE_MACHINE_ADDR sm->addr + +static const struct eapol_callbacks eapol_cb; + +/* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */ + +#define setPortAuthorized() \ +sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 1) +#define setPortUnauthorized() \ +sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 0) + +/* procedures */ +#define txCannedFail() eapol_auth_tx_canned_eap(sm, 0) +#define txCannedSuccess() eapol_auth_tx_canned_eap(sm, 1) +#define txReq() eapol_auth_tx_req(sm) +#define abortAuth() sm->eapol->cb.abort_auth(sm->eapol->conf.ctx, sm->sta) +#define txKey() sm->eapol->cb.tx_key(sm->eapol->conf.ctx, sm->sta) +#define processKey() do { } while (0) + + +static void eapol_sm_step_run(struct eapol_state_machine *sm); +static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); +static void eapol_auth_initialize(struct eapol_state_machine *sm); +static void eapol_auth_conf_free(struct eapol_auth_config *conf); + +#ifndef ESP_SUPPLICANT +static void eapol_auth_logger(struct eapol_authenticator *eapol, + const u8 *addr, eapol_logger_level level, + const char *txt) +{ + if (eapol->cb.logger == NULL) + return; + eapol->cb.logger(eapol->conf.ctx, addr, level, txt); +} + + +PRINTF_FORMAT(4, 5) +static void eapol_auth_vlogger(struct eapol_authenticator *eapol, + const u8 *addr, eapol_logger_level level, + const char *fmt, ...) +{ + char *format; + int maxlen; + va_list ap; + + if (eapol->cb.logger == NULL) + return; + + maxlen = os_strlen(fmt) + 100; + format = os_malloc(maxlen); + if (!format) + return; + + va_start(ap, fmt); + vsnprintf(format, maxlen, fmt, ap); + va_end(ap); + + eapol_auth_logger(eapol, addr, level, format); + + os_free(format); +} +#else + +#define eapol_auth_logger(...) do {} while(0) +#define eapol_auth_vlogger(...) do {} while (0) +#endif + +static void eapol_auth_tx_canned_eap(struct eapol_state_machine *sm, + int success) +{ + struct eap_hdr eap; + + os_memset(&eap, 0, sizeof(eap)); + + eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; + eap.identifier = ++sm->last_eap_id; + eap.length = host_to_be16(sizeof(eap)); + + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, + "Sending canned EAP packet %s (identifier %d)", + success ? "SUCCESS" : "FAILURE", eap.identifier); + sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta, + IEEE802_1X_TYPE_EAP_PACKET, + (u8 *) &eap, sizeof(eap)); + sm->dot1xAuthEapolFramesTx++; +} + + +static void eapol_auth_tx_req(struct eapol_state_machine *sm) +{ + if (sm->eap_if->eapReqData == NULL || + wpabuf_len(sm->eap_if->eapReqData) < sizeof(struct eap_hdr)) { + eapol_auth_logger(sm->eapol, sm->addr, + EAPOL_LOGGER_DEBUG, + "TxReq called, but there is no EAP request " + "from authentication server"); + return; + } + + if (sm->flags & EAPOL_SM_WAIT_START) { + wpa_printf(MSG_DEBUG, "EAPOL: Drop EAPOL TX to " MACSTR + " while waiting for EAPOL-Start", + MAC2STR(sm->addr)); + return; + } + + sm->last_eap_id = eap_get_id(sm->eap_if->eapReqData); + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, + "Sending EAP Packet (identifier %d)", + sm->last_eap_id); + sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta, + IEEE802_1X_TYPE_EAP_PACKET, + wpabuf_head(sm->eap_if->eapReqData), + wpabuf_len(sm->eap_if->eapReqData)); + sm->dot1xAuthEapolFramesTx++; + if (eap_get_type(sm->eap_if->eapReqData) == EAP_TYPE_IDENTITY) + sm->dot1xAuthEapolReqIdFramesTx++; + else + sm->dot1xAuthEapolReqFramesTx++; +} + + +/** + * eapol_port_timers_tick - Port Timers state machine + * @eloop_ctx: struct eapol_state_machine * + * @timeout_ctx: Not used + * + * This statemachine is implemented as a function that will be called + * once a second as a registered event loop timeout. + */ +static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_state_machine *state = timeout_ctx; + + if (state->aWhile > 0) { + state->aWhile--; + if (state->aWhile == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - aWhile --> 0", + MAC2STR(state->addr)); + } + } + + if (state->quietWhile > 0) { + state->quietWhile--; + if (state->quietWhile == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - quietWhile --> 0", + MAC2STR(state->addr)); + } + } + + if (state->reAuthWhen > 0) { + state->reAuthWhen--; + if (state->reAuthWhen == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - reAuthWhen --> 0", + MAC2STR(state->addr)); + } + } + + if (state->eap_if->retransWhile > 0) { + state->eap_if->retransWhile--; + if (state->eap_if->retransWhile == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - (EAP) retransWhile --> 0", + MAC2STR(state->addr)); + } + } + + eapol_sm_step_run(state); + + eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state); +} + + + +/* Authenticator PAE state machine */ + +SM_STATE(AUTH_PAE, INITIALIZE) +{ + SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae); + sm->portMode = Auto; + + /* + * Clearing keyRun here is not specified in IEEE Std 802.1X-2004, but + * it looks like this would be logical thing to do here since the + * EAPOL-Key exchange is not possible in this state. It is possible to + * get here on disconnection event without advancing to the + * AUTHENTICATING state to clear keyRun before the IEEE 802.11 RSN + * authenticator state machine runs and that may advance from + * AUTHENTICATION2 to INITPMK if keyRun = true has been left from the + * last association. This can be avoided by clearing keyRun here. + */ + sm->keyRun = false; +} + + +SM_STATE(AUTH_PAE, DISCONNECTED) +{ + int from_initialize = sm->auth_pae_state == AUTH_PAE_INITIALIZE; + + if (sm->eapolLogoff) { + if (sm->auth_pae_state == AUTH_PAE_CONNECTING) + sm->authEapLogoffsWhileConnecting++; + else if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) + sm->authAuthEapLogoffWhileAuthenticated++; + } + + SM_ENTRY_MA(AUTH_PAE, DISCONNECTED, auth_pae); + + sm->authPortStatus = Unauthorized; + setPortUnauthorized(); + sm->reAuthCount = 0; + sm->eapolLogoff = false; + if (!from_initialize) { + sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, + sm->flags & EAPOL_SM_PREAUTH, + sm->remediation); + } +} + + +SM_STATE(AUTH_PAE, RESTART) +{ + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) { + if (sm->reAuthenticate) + sm->authAuthReauthsWhileAuthenticated++; + if (sm->eapolStart) + sm->authAuthEapStartsWhileAuthenticated++; + if (sm->eapolLogoff) + sm->authAuthEapLogoffWhileAuthenticated++; + } + + SM_ENTRY_MA(AUTH_PAE, RESTART, auth_pae); + + sm->eap_if->eapRestart = true; +} + + +SM_STATE(AUTH_PAE, CONNECTING) +{ + if (sm->auth_pae_state != AUTH_PAE_CONNECTING) + sm->authEntersConnecting++; + + SM_ENTRY_MA(AUTH_PAE, CONNECTING, auth_pae); + + sm->reAuthenticate = false; + sm->reAuthCount++; +} + + +SM_STATE(AUTH_PAE, HELD) +{ + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authFail) + sm->authAuthFailWhileAuthenticating++; + + SM_ENTRY_MA(AUTH_PAE, HELD, auth_pae); + + sm->authPortStatus = Unauthorized; + setPortUnauthorized(); + sm->quietWhile = sm->quietPeriod; + sm->eapolLogoff = false; + + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_WARNING, + "authentication failed - EAP type: %d (%s)", + sm->eap_type_authsrv, + eap_server_get_name(0, sm->eap_type_authsrv)); + if (sm->eap_type_authsrv != sm->eap_type_supp) { + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO, + "Supplicant used different EAP type: " + "%d (%s)", sm->eap_type_supp, + eap_server_get_name(0, sm->eap_type_supp)); + } + sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, + sm->flags & EAPOL_SM_PREAUTH, sm->remediation); +} + + +SM_STATE(AUTH_PAE, AUTHENTICATED) +{ + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess) + sm->authAuthSuccessesWhileAuthenticating++; + + SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae); + + sm->authPortStatus = Authorized; + setPortAuthorized(); + sm->reAuthCount = 0; + sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 1, + sm->flags & EAPOL_SM_PREAUTH, sm->remediation); +} + + +SM_STATE(AUTH_PAE, AUTHENTICATING) +{ + SM_ENTRY_MA(AUTH_PAE, AUTHENTICATING, auth_pae); + + sm->eapolStart = false; + sm->authSuccess = false; + sm->authFail = false; + sm->authTimeout = false; + sm->authStart = true; + sm->keyRun = false; + sm->keyDone = false; +} + + +SM_STATE(AUTH_PAE, ABORTING) +{ + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING) { + if (sm->authTimeout) + sm->authAuthTimeoutsWhileAuthenticating++; + if (sm->eapolStart) + sm->authAuthEapStartsWhileAuthenticating++; + if (sm->eapolLogoff) + sm->authAuthEapLogoffWhileAuthenticating++; + } + + SM_ENTRY_MA(AUTH_PAE, ABORTING, auth_pae); + + sm->authAbort = true; + sm->keyRun = false; + sm->keyDone = false; +} + + +SM_STATE(AUTH_PAE, FORCE_AUTH) +{ + SM_ENTRY_MA(AUTH_PAE, FORCE_AUTH, auth_pae); + + sm->authPortStatus = Authorized; + setPortAuthorized(); + sm->portMode = ForceAuthorized; + sm->eapolStart = false; + txCannedSuccess(); +} + + +SM_STATE(AUTH_PAE, FORCE_UNAUTH) +{ + SM_ENTRY_MA(AUTH_PAE, FORCE_UNAUTH, auth_pae); + + sm->authPortStatus = Unauthorized; + setPortUnauthorized(); + sm->portMode = ForceUnauthorized; + sm->eapolStart = false; + txCannedFail(); +} + + +SM_STEP(AUTH_PAE) +{ + if ((sm->portControl == Auto && sm->portMode != sm->portControl) || + sm->initialize || !sm->eap_if->portEnabled) + SM_ENTER_GLOBAL(AUTH_PAE, INITIALIZE); + else if (sm->portControl == ForceAuthorized && + sm->portMode != sm->portControl && + !(sm->initialize || !sm->eap_if->portEnabled)) + SM_ENTER_GLOBAL(AUTH_PAE, FORCE_AUTH); + else if (sm->portControl == ForceUnauthorized && + sm->portMode != sm->portControl && + !(sm->initialize || !sm->eap_if->portEnabled)) + SM_ENTER_GLOBAL(AUTH_PAE, FORCE_UNAUTH); + else { + switch (sm->auth_pae_state) { + case AUTH_PAE_INITIALIZE: + SM_ENTER(AUTH_PAE, DISCONNECTED); + break; + case AUTH_PAE_DISCONNECTED: + SM_ENTER(AUTH_PAE, RESTART); + break; + case AUTH_PAE_RESTART: + if (!sm->eap_if->eapRestart) + SM_ENTER(AUTH_PAE, CONNECTING); + break; + case AUTH_PAE_HELD: + if (sm->quietWhile == 0) + SM_ENTER(AUTH_PAE, RESTART); + break; + case AUTH_PAE_CONNECTING: + if (sm->eapolLogoff || sm->reAuthCount > sm->reAuthMax) + SM_ENTER(AUTH_PAE, DISCONNECTED); + else if ((sm->eap_if->eapReq && + sm->reAuthCount <= sm->reAuthMax) || + sm->eap_if->eapSuccess || sm->eap_if->eapFail) + SM_ENTER(AUTH_PAE, AUTHENTICATING); + break; + case AUTH_PAE_AUTHENTICATED: + if (sm->eapolStart || sm->reAuthenticate) + SM_ENTER(AUTH_PAE, RESTART); + else if (sm->eapolLogoff || !sm->portValid) + SM_ENTER(AUTH_PAE, DISCONNECTED); + break; + case AUTH_PAE_AUTHENTICATING: + if (sm->authSuccess && sm->portValid) + SM_ENTER(AUTH_PAE, AUTHENTICATED); + else if (sm->authFail || + (sm->keyDone && !sm->portValid)) + SM_ENTER(AUTH_PAE, HELD); + else if (sm->eapolStart || sm->eapolLogoff || + sm->authTimeout) + SM_ENTER(AUTH_PAE, ABORTING); + break; + case AUTH_PAE_ABORTING: + if (sm->eapolLogoff && !sm->authAbort) + SM_ENTER(AUTH_PAE, DISCONNECTED); + else if (!sm->eapolLogoff && !sm->authAbort) + SM_ENTER(AUTH_PAE, RESTART); + break; + case AUTH_PAE_FORCE_AUTH: + if (sm->eapolStart) + SM_ENTER(AUTH_PAE, FORCE_AUTH); + break; + case AUTH_PAE_FORCE_UNAUTH: + if (sm->eapolStart) + SM_ENTER(AUTH_PAE, FORCE_UNAUTH); + break; + } + } +} + + + +/* Backend Authentication state machine */ + +SM_STATE(BE_AUTH, INITIALIZE) +{ + SM_ENTRY_MA(BE_AUTH, INITIALIZE, be_auth); + + abortAuth(); + sm->eap_if->eapNoReq = false; + sm->authAbort = false; +} + + +SM_STATE(BE_AUTH, REQUEST) +{ + SM_ENTRY_MA(BE_AUTH, REQUEST, be_auth); + + txReq(); + sm->eap_if->eapReq = false; + sm->backendOtherRequestsToSupplicant++; + + /* + * Clearing eapolEap here is not specified in IEEE Std 802.1X-2004, but + * it looks like this would be logical thing to do there since the old + * EAP response would not be valid anymore after the new EAP request + * was sent out. + * + * A race condition has been reported, in which hostapd ended up + * sending out EAP-Response/Identity as a response to the first + * EAP-Request from the main EAP method. This can be avoided by + * clearing eapolEap here. + */ + sm->eapolEap = false; +} + + +SM_STATE(BE_AUTH, RESPONSE) +{ + SM_ENTRY_MA(BE_AUTH, RESPONSE, be_auth); + + sm->authTimeout = false; + sm->eapolEap = false; + sm->eap_if->eapNoReq = false; + sm->aWhile = sm->serverTimeout; + sm->eap_if->eapResp = true; + /* sendRespToServer(); */ + sm->backendResponses++; +} + + +SM_STATE(BE_AUTH, SUCCESS) +{ + SM_ENTRY_MA(BE_AUTH, SUCCESS, be_auth); + + txReq(); + sm->authSuccess = true; + sm->keyRun = true; +} + + +SM_STATE(BE_AUTH, FAIL) +{ + SM_ENTRY_MA(BE_AUTH, FAIL, be_auth); + + txReq(); + sm->authFail = true; +} + + +SM_STATE(BE_AUTH, TIMEOUT) +{ + SM_ENTRY_MA(BE_AUTH, TIMEOUT, be_auth); + + sm->authTimeout = true; +} + + +SM_STATE(BE_AUTH, IDLE) +{ + SM_ENTRY_MA(BE_AUTH, IDLE, be_auth); + + sm->authStart = false; +} + + +SM_STATE(BE_AUTH, IGNORE) +{ + SM_ENTRY_MA(BE_AUTH, IGNORE, be_auth); + + sm->eap_if->eapNoReq = false; +} + + +SM_STEP(BE_AUTH) +{ + if (sm->portControl != Auto || sm->initialize || sm->authAbort) { + SM_ENTER_GLOBAL(BE_AUTH, INITIALIZE); + return; + } + + switch (sm->be_auth_state) { + case BE_AUTH_INITIALIZE: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_REQUEST: + if (sm->eapolEap) + SM_ENTER(BE_AUTH, RESPONSE); + else if (sm->eap_if->eapReq) + SM_ENTER(BE_AUTH, REQUEST); + else if (sm->eap_if->eapTimeout) + SM_ENTER(BE_AUTH, TIMEOUT); + break; + case BE_AUTH_RESPONSE: + if (sm->eap_if->eapNoReq) + SM_ENTER(BE_AUTH, IGNORE); + if (sm->eap_if->eapReq) { + sm->backendAccessChallenges++; + SM_ENTER(BE_AUTH, REQUEST); + } else if (sm->aWhile == 0) + SM_ENTER(BE_AUTH, TIMEOUT); + else if (sm->eap_if->eapFail) { + sm->backendAuthFails++; + SM_ENTER(BE_AUTH, FAIL); + } else if (sm->eap_if->eapSuccess) { + sm->backendAuthSuccesses++; + SM_ENTER(BE_AUTH, SUCCESS); + } + break; + case BE_AUTH_SUCCESS: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_FAIL: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_TIMEOUT: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_IDLE: + if (sm->eap_if->eapFail && sm->authStart) + SM_ENTER(BE_AUTH, FAIL); + else if (sm->eap_if->eapReq && sm->authStart) + SM_ENTER(BE_AUTH, REQUEST); + else if (sm->eap_if->eapSuccess && sm->authStart) + SM_ENTER(BE_AUTH, SUCCESS); + break; + case BE_AUTH_IGNORE: + if (sm->eapolEap) + SM_ENTER(BE_AUTH, RESPONSE); + else if (sm->eap_if->eapReq) + SM_ENTER(BE_AUTH, REQUEST); + else if (sm->eap_if->eapTimeout) + SM_ENTER(BE_AUTH, TIMEOUT); + break; + } +} + + + +/* Reauthentication Timer state machine */ + +SM_STATE(REAUTH_TIMER, INITIALIZE) +{ + SM_ENTRY_MA(REAUTH_TIMER, INITIALIZE, reauth_timer); + + sm->reAuthWhen = sm->reAuthPeriod; +} + + +SM_STATE(REAUTH_TIMER, REAUTHENTICATE) +{ + SM_ENTRY_MA(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); + + sm->reAuthenticate = true; + sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta, + EAPOL_AUTH_REAUTHENTICATE); +} + + +SM_STEP(REAUTH_TIMER) +{ + if (sm->portControl != Auto || sm->initialize || + sm->authPortStatus == Unauthorized || !sm->reAuthEnabled) { + SM_ENTER_GLOBAL(REAUTH_TIMER, INITIALIZE); + return; + } + + switch (sm->reauth_timer_state) { + case REAUTH_TIMER_INITIALIZE: + if (sm->reAuthWhen == 0) + SM_ENTER(REAUTH_TIMER, REAUTHENTICATE); + break; + case REAUTH_TIMER_REAUTHENTICATE: + SM_ENTER(REAUTH_TIMER, INITIALIZE); + break; + } +} + + + +#ifdef CONFIG_WEP + +/* Authenticator Key Transmit state machine */ + +SM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT) +{ + SM_ENTRY_MA(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx); +} + + +SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT) +{ + SM_ENTRY_MA(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx); + + txKey(); + sm->eap_if->eapKeyAvailable = false; + sm->keyDone = true; +} + + +SM_STEP(AUTH_KEY_TX) +{ + if (sm->initialize || sm->portControl != Auto) { + SM_ENTER_GLOBAL(AUTH_KEY_TX, NO_KEY_TRANSMIT); + return; + } + + switch (sm->auth_key_tx_state) { + case AUTH_KEY_TX_NO_KEY_TRANSMIT: + if (sm->keyTxEnabled && sm->eap_if->eapKeyAvailable && + sm->keyRun && !(sm->flags & EAPOL_SM_USES_WPA)) + SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); + break; + case AUTH_KEY_TX_KEY_TRANSMIT: + if (!sm->keyTxEnabled || !sm->keyRun) + SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT); + else if (sm->eap_if->eapKeyAvailable) + SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); + break; + } +} + + + +/* Key Receive state machine */ + +SM_STATE(KEY_RX, NO_KEY_RECEIVE) +{ + SM_ENTRY_MA(KEY_RX, NO_KEY_RECEIVE, key_rx); +} + + +SM_STATE(KEY_RX, KEY_RECEIVE) +{ + SM_ENTRY_MA(KEY_RX, KEY_RECEIVE, key_rx); + + processKey(); + sm->rxKey = false; +} + + +SM_STEP(KEY_RX) +{ + if (sm->initialize || !sm->eap_if->portEnabled) { + SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); + return; + } + + switch (sm->key_rx_state) { + case KEY_RX_NO_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + case KEY_RX_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + } +} + +#endif /* CONFIG_WEP */ + + + +/* Controlled Directions state machine */ + +SM_STATE(CTRL_DIR, FORCE_BOTH) +{ + SM_ENTRY_MA(CTRL_DIR, FORCE_BOTH, ctrl_dir); + sm->operControlledDirections = Both; +} + + +SM_STATE(CTRL_DIR, IN_OR_BOTH) +{ + SM_ENTRY_MA(CTRL_DIR, IN_OR_BOTH, ctrl_dir); + sm->operControlledDirections = sm->adminControlledDirections; +} + + +SM_STEP(CTRL_DIR) +{ + if (sm->initialize) { + SM_ENTER_GLOBAL(CTRL_DIR, IN_OR_BOTH); + return; + } + + switch (sm->ctrl_dir_state) { + case CTRL_DIR_FORCE_BOTH: + if (sm->eap_if->portEnabled && sm->operEdge) + SM_ENTER(CTRL_DIR, IN_OR_BOTH); + break; + case CTRL_DIR_IN_OR_BOTH: + if (sm->operControlledDirections != + sm->adminControlledDirections) + SM_ENTER(CTRL_DIR, IN_OR_BOTH); + if (!sm->eap_if->portEnabled || !sm->operEdge) + SM_ENTER(CTRL_DIR, FORCE_BOTH); + break; + } +} + + + +struct eapol_state_machine * +eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, + int flags, const struct wpabuf *assoc_wps_ie, + const struct wpabuf *assoc_p2p_ie, void *sta_ctx, + const char *identity, const char *radius_cui) +{ + struct eapol_state_machine *sm; + struct eap_session_data eap_sess; + + if (eapol == NULL) + return NULL; + + sm = os_zalloc(sizeof(*sm)); + if (sm == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X state machine allocation " + "failed"); + return NULL; + } + sm->radius_identifier = -1; + os_memcpy(sm->addr, addr, ETH_ALEN); + sm->flags = flags; + + sm->eapol = eapol; + sm->sta = sta_ctx; + + /* Set default values for state machine constants */ + sm->auth_pae_state = AUTH_PAE_INITIALIZE; + sm->quietPeriod = AUTH_PAE_DEFAULT_quietPeriod; + sm->reAuthMax = AUTH_PAE_DEFAULT_reAuthMax; + + sm->be_auth_state = BE_AUTH_INITIALIZE; + sm->serverTimeout = BE_AUTH_DEFAULT_serverTimeout; + + sm->reauth_timer_state = REAUTH_TIMER_INITIALIZE; + sm->reAuthPeriod = eapol->conf.eap_reauth_period; + sm->reAuthEnabled = eapol->conf.eap_reauth_period > 0; + + sm->auth_key_tx_state = AUTH_KEY_TX_NO_KEY_TRANSMIT; + + sm->key_rx_state = KEY_RX_NO_KEY_RECEIVE; + + sm->ctrl_dir_state = CTRL_DIR_IN_OR_BOTH; + + sm->portControl = Auto; + +#ifdef CONFIG_WEP + if (!eapol->conf.wpa && + (eapol->default_wep_key || eapol->conf.individual_wep_key_len > 0)) + sm->keyTxEnabled = true; + else +#endif /* CONFIG_WEP */ + sm->keyTxEnabled = false; + if (eapol->conf.wpa) + sm->portValid = false; + else + sm->portValid = true; + + os_memset(&eap_sess, 0, sizeof(eap_sess)); + eap_sess.assoc_wps_ie = assoc_wps_ie; + eap_sess.assoc_p2p_ie = assoc_p2p_ie; + eap_sess.peer_addr = addr; + sm->eap = eap_server_sm_init(sm, &eapol_cb, eapol->conf.eap_cfg, + &eap_sess); + if (sm->eap == NULL) { + eapol_auth_free(sm); + return NULL; + } + sm->eap_if = eap_get_interface(sm->eap); + + eapol_auth_initialize(sm); + + if (identity) { + sm->identity = (u8 *) os_strdup(identity); + if (sm->identity) + sm->identity_len = os_strlen(identity); + } +#ifndef CONFIG_NO_RADIUS + if (radius_cui) + sm->radius_cui = wpabuf_alloc_copy(radius_cui, + os_strlen(radius_cui)); + + 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; +} + + +void eapol_auth_free(struct eapol_state_machine *sm) +{ + if (sm == NULL) + return; + + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL); + if (sm->eap) + eap_server_sm_deinit(sm->eap); + +#ifndef CONFIG_NO_RADIUS + wpabuf_free(sm->radius_cui); +#endif /* CONFIG_NO_RADIUS */ + os_free(sm->identity); + os_free(sm); +} + + +static int eapol_sm_sta_entry_alive(struct eapol_authenticator *eapol, + const u8 *addr) +{ + return eapol->cb.sta_entry_alive(eapol->conf.ctx, addr); +} + + +static void eapol_sm_step_run(struct eapol_state_machine *sm) +{ + struct eapol_authenticator *eapol = sm->eapol; + u8 addr[ETH_ALEN]; + unsigned int prev_auth_pae, prev_be_auth, prev_reauth_timer, + prev_auth_key_tx, prev_key_rx, prev_ctrl_dir; + int max_steps = 100; + + os_memcpy(addr, sm->addr, ETH_ALEN); + + /* + * Allow EAPOL state machines to run as long as there are state + * changes, but exit and return here through event loop if more than + * 100 steps is needed as a precaution against infinite loops inside + * eloop callback. + */ +restart: + prev_auth_pae = sm->auth_pae_state; + prev_be_auth = sm->be_auth_state; + prev_reauth_timer = sm->reauth_timer_state; + prev_auth_key_tx = sm->auth_key_tx_state; + prev_key_rx = sm->key_rx_state; + prev_ctrl_dir = sm->ctrl_dir_state; + + SM_STEP_RUN(AUTH_PAE); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(BE_AUTH); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(REAUTH_TIMER); +#ifdef CONFIG_WEP + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(AUTH_KEY_TX); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(KEY_RX); +#endif /* CONFIG_WEP */ + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(CTRL_DIR); + + if (prev_auth_pae != sm->auth_pae_state || + prev_be_auth != sm->be_auth_state || + prev_reauth_timer != sm->reauth_timer_state || + prev_auth_key_tx != sm->auth_key_tx_state || + prev_key_rx != sm->key_rx_state || + prev_ctrl_dir != sm->ctrl_dir_state) { + if (--max_steps > 0) + goto restart; + /* Re-run from eloop timeout */ + eapol_auth_step(sm); + return; + } + + if (eapol_sm_sta_entry_alive(eapol, addr) && sm->eap) { + if (eap_server_sm_step(sm->eap)) { + if (--max_steps > 0) + goto restart; + /* Re-run from eloop timeout */ + eapol_auth_step(sm); + return; + } + +#ifndef ESP_SUPPLICANT + /* TODO: find a better location for this */ + if (sm->eap_if->aaaEapResp) { + sm->eap_if->aaaEapResp = false; + if (sm->eap_if->aaaEapRespData == NULL) { + wpa_printf(MSG_DEBUG, "EAPOL: aaaEapResp set, " + "but no aaaEapRespData available"); + return; + } + sm->eapol->cb.aaa_send( + sm->eapol->conf.ctx, sm->sta, + wpabuf_head(sm->eap_if->aaaEapRespData), + wpabuf_len(sm->eap_if->aaaEapRespData)); + } +#endif + } + + if (eapol_sm_sta_entry_alive(eapol, addr)) + sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta, + EAPOL_AUTH_SM_CHANGE); +} + + +static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_state_machine *sm = eloop_ctx; + eapol_sm_step_run(sm); +} + + +/** + * eapol_auth_step - Advance EAPOL state machines + * @sm: EAPOL state machine + * + * This function is called to advance EAPOL state machines after any change + * that could affect their state. + */ +void eapol_auth_step(struct eapol_state_machine *sm) +{ + /* + * Run eapol_sm_step_run from a registered timeout to make sure that + * other possible timeouts/events are processed and to avoid long + * function call chains. + */ + + eloop_register_timeout(0, 0, eapol_sm_step_cb, sm, NULL); +} + + +static void eapol_auth_initialize(struct eapol_state_machine *sm) +{ + sm->initializing = true; + /* Initialize the state machines by asserting initialize and then + * deasserting it after one step */ + sm->initialize = true; + eapol_sm_step_run(sm); + sm->initialize = false; + eapol_sm_step_run(sm); + sm->initializing = false; + + /* Start one second tick for port timers state machine */ + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); +} + + +static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) +{ + struct eapol_state_machine *sm = ctx; + int ret; + + ret = sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity, + identity_len, phase2, user); + if (user->remediation) + sm->remediation = 1; + return ret; +} + + +static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) +{ + struct eapol_state_machine *sm = ctx; + *len = sm->eapol->conf.eap_req_id_text_len; + return sm->eapol->conf.eap_req_id_text; +} + + +static int eapol_sm_get_erp_send_reauth_start(void *ctx) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->conf.erp_send_reauth_start; +} + + +static const char * eapol_sm_get_erp_domain(void *ctx) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->conf.erp_domain; +} + + +static struct eap_server_erp_key * eapol_sm_erp_get_key(void *ctx, + const char *keyname) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->cb.erp_get_key(sm->eapol->conf.ctx, keyname); +} + + +static int eapol_sm_erp_add_key(void *ctx, struct eap_server_erp_key *erp) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->cb.erp_add_key(sm->eapol->conf.ctx, erp); +} + + +static const struct eapol_callbacks eapol_cb = +{ + eapol_sm_get_eap_user, + eapol_sm_get_eap_req_id_text, + NULL, + eapol_sm_get_erp_send_reauth_start, + eapol_sm_get_erp_domain, + eapol_sm_erp_get_key, + eapol_sm_erp_add_key, +}; + + +int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) +{ + if (sm == NULL || ctx == NULL || ctx != sm->eap) + return -1; + + eap_sm_pending_cb(sm->eap); + eapol_auth_step(sm); + + return 0; +} + + +void eapol_auth_reauthenticate(struct eapol_state_machine *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: External reauthentication trigger for " + MACSTR, MAC2STR(sm->addr)); + sm->reAuthenticate = true; + eapol_auth_step(sm); +} + + +int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param, + const char *value) +{ + wpa_printf(MSG_DEBUG, "EAPOL: External configuration operation for " + MACSTR " - param=%s value=%s", + MAC2STR(sm->addr), param, value); + + if (os_strcasecmp(param, "AdminControlledDirections") == 0) { + if (os_strcmp(value, "Both") == 0) + sm->adminControlledDirections = Both; + else if (os_strcmp(value, "In") == 0) + sm->adminControlledDirections = In; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + if (os_strcasecmp(param, "AdminControlledPortControl") == 0) { + if (os_strcmp(value, "ForceAuthorized") == 0) + sm->portControl = ForceAuthorized; + else if (os_strcmp(value, "ForceUnauthorized") == 0) + sm->portControl = ForceUnauthorized; + else if (os_strcmp(value, "Auto") == 0) + sm->portControl = Auto; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + if (os_strcasecmp(param, "quietPeriod") == 0) { + sm->quietPeriod = atoi(value); + return 0; + } + + if (os_strcasecmp(param, "serverTimeout") == 0) { + sm->serverTimeout = atoi(value); + return 0; + } + + if (os_strcasecmp(param, "reAuthPeriod") == 0) { + sm->reAuthPeriod = atoi(value); + return 0; + } + + if (os_strcasecmp(param, "reAuthEnabled") == 0) { + if (os_strcmp(value, "TRUE") == 0) + sm->reAuthEnabled = true; + else if (os_strcmp(value, "FALSE") == 0) + sm->reAuthEnabled = false; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + if (os_strcasecmp(param, "KeyTransmissionEnabled") == 0) { + if (os_strcmp(value, "TRUE") == 0) + sm->keyTxEnabled = true; + else if (os_strcmp(value, "FALSE") == 0) + sm->keyTxEnabled = false; + else + return -1; + eapol_auth_step(sm); + return 0; + } + + return -1; +} + + +static int eapol_auth_conf_clone(struct eapol_auth_config *dst, + struct eapol_auth_config *src) +{ + dst->eap_cfg = src->eap_cfg; + dst->ctx = src->ctx; + dst->eap_reauth_period = src->eap_reauth_period; + dst->wpa = src->wpa; +#ifdef CONFIG_WEP + dst->individual_wep_key_len = src->individual_wep_key_len; +#endif /* CONFIG_WEP */ + os_free(dst->eap_req_id_text); + if (src->eap_req_id_text) { + dst->eap_req_id_text = os_memdup(src->eap_req_id_text, + src->eap_req_id_text_len); + if (dst->eap_req_id_text == NULL) + return -1; + dst->eap_req_id_text_len = src->eap_req_id_text_len; + } else { + dst->eap_req_id_text = NULL; + dst->eap_req_id_text_len = 0; + } + + os_free(dst->erp_domain); + if (src->erp_domain) { + dst->erp_domain = os_strdup(src->erp_domain); + if (dst->erp_domain == NULL) + goto fail; + } else { + dst->erp_domain = NULL; + } + dst->erp_send_reauth_start = src->erp_send_reauth_start; + + return 0; + +fail: + eapol_auth_conf_free(dst); + return -1; +} + + +static void eapol_auth_conf_free(struct eapol_auth_config *conf) +{ + os_free(conf->eap_req_id_text); + conf->eap_req_id_text = NULL; + os_free(conf->erp_domain); + conf->erp_domain = NULL; +} + + +struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, + struct eapol_auth_cb *cb) +{ + struct eapol_authenticator *eapol; + + eapol = os_zalloc(sizeof(*eapol)); + if (eapol == NULL) + return NULL; + + if (eapol_auth_conf_clone(&eapol->conf, conf) < 0) { + os_free(eapol); + return NULL; + } + +#ifdef CONFIG_WEP + if (conf->individual_wep_key_len > 0) { + /* use key0 in individual key and key1 in broadcast key */ + eapol->default_wep_key_idx = 1; + } +#endif /* CONFIG_WEP */ + + eapol->cb.eapol_send = cb->eapol_send; + eapol->cb.aaa_send = cb->aaa_send; + eapol->cb.finished = cb->finished; + eapol->cb.get_eap_user = cb->get_eap_user; + eapol->cb.sta_entry_alive = cb->sta_entry_alive; + eapol->cb.logger = cb->logger; + eapol->cb.set_port_authorized = cb->set_port_authorized; + eapol->cb.abort_auth = cb->abort_auth; + eapol->cb.tx_key = cb->tx_key; + eapol->cb.eapol_event = cb->eapol_event; + eapol->cb.erp_get_key = cb->erp_get_key; + eapol->cb.erp_add_key = cb->erp_add_key; + + return eapol; +} + + +void eapol_auth_deinit(struct eapol_authenticator *eapol) +{ + if (eapol == NULL) + return; + + eapol_auth_conf_free(&eapol->conf); +#ifdef CONFIG_WEP + os_free(eapol->default_wep_key); +#endif /* CONFIG_WEP */ + os_free(eapol); +} diff --git a/components/wpa_supplicant/src/eapol_auth/eapol_auth_sm.h b/components/wpa_supplicant/src/eapol_auth/eapol_auth_sm.h new file mode 100644 index 0000000000..5fe89c64b3 --- /dev/null +++ b/components/wpa_supplicant/src/eapol_auth/eapol_auth_sm.h @@ -0,0 +1,82 @@ +/* + * IEEE 802.1X-2004 Authenticator - EAPOL state machine + * Copyright (c) 2002-2015, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAPOL_AUTH_SM_H +#define EAPOL_AUTH_SM_H + +#define EAPOL_SM_PREAUTH BIT(0) +#define EAPOL_SM_WAIT_START BIT(1) +#define EAPOL_SM_USES_WPA BIT(2) +#define EAPOL_SM_FROM_PMKSA_CACHE BIT(3) + +struct eapol_auth_config { + const struct eap_config *eap_cfg; + int eap_reauth_period; + int wpa; + int individual_wep_key_len; + char *eap_req_id_text; /* a copy of this will be allocated */ + size_t eap_req_id_text_len; + int erp_send_reauth_start; + char *erp_domain; /* a copy of this will be allocated */ + + /* Opaque context pointer to owner data for callback functions */ + void *ctx; +}; + +struct eap_user; +struct eap_server_erp_key; + +typedef enum { + EAPOL_LOGGER_DEBUG, EAPOL_LOGGER_INFO, EAPOL_LOGGER_WARNING +} eapol_logger_level; + +enum eapol_event { + EAPOL_AUTH_SM_CHANGE, + EAPOL_AUTH_REAUTHENTICATE +}; + +struct eapol_auth_cb { + void (*eapol_send)(void *ctx, void *sta_ctx, u8 type, const u8 *data, + size_t datalen); + void (*aaa_send)(void *ctx, void *sta_ctx, const u8 *data, + size_t datalen); + void (*finished)(void *ctx, void *sta_ctx, int success, int preauth, + int remediation); + int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, + int phase2, struct eap_user *user); + int (*sta_entry_alive)(void *ctx, const u8 *addr); + void (*logger)(void *ctx, const u8 *addr, eapol_logger_level level, + const char *txt); + void (*set_port_authorized)(void *ctx, void *sta_ctx, int authorized); + void (*abort_auth)(void *ctx, void *sta_ctx); + void (*tx_key)(void *ctx, void *sta_ctx); + void (*eapol_event)(void *ctx, void *sta_ctx, enum eapol_event type); + struct eap_server_erp_key * (*erp_get_key)(void *ctx, + const char *keyname); + int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp); +}; + + +struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, + struct eapol_auth_cb *cb); +void eapol_auth_deinit(struct eapol_authenticator *eapol); +struct eapol_state_machine * +eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, + int flags, const struct wpabuf *assoc_wps_ie, + const struct wpabuf *assoc_p2p_ie, void *sta_ctx, + const char *identity, const char *radius_cui); +void eapol_auth_free(struct eapol_state_machine *sm); +void eapol_auth_step(struct eapol_state_machine *sm); +int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, + size_t buflen); +int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx); +void eapol_auth_reauthenticate(struct eapol_state_machine *sm); +int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param, + const char *value); + +#endif /* EAPOL_AUTH_SM_H */ diff --git a/components/wpa_supplicant/src/eapol_auth/eapol_auth_sm_i.h b/components/wpa_supplicant/src/eapol_auth/eapol_auth_sm_i.h new file mode 100644 index 0000000000..a8a5850c96 --- /dev/null +++ b/components/wpa_supplicant/src/eapol_auth/eapol_auth_sm_i.h @@ -0,0 +1,176 @@ +/* + * IEEE 802.1X-2004 Authenticator - EAPOL state machine (internal definitions) + * Copyright (c) 2002-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAPOL_AUTH_SM_I_H +#define EAPOL_AUTH_SM_I_H + +#include "common/defs.h" + +/* IEEE Std 802.1X-2004, Ch. 8.2 */ + +typedef enum { ForceUnauthorized = 1, ForceAuthorized = 3, Auto = 2 } + PortTypes; +typedef enum { Unauthorized = 2, Authorized = 1 } PortState; +typedef enum { Both = 0, In = 1 } ControlledDirection; +typedef unsigned int Counter; + + +/** + * struct eapol_authenticator - Global EAPOL authenticator data + */ +struct eapol_authenticator { + struct eapol_auth_config conf; + struct eapol_auth_cb cb; + + u8 *default_wep_key; + u8 default_wep_key_idx; +}; + + +/** + * struct eapol_state_machine - Per-Supplicant Authenticator state machines + */ +struct eapol_state_machine { + /* timers */ + int aWhile; + int quietWhile; + int reAuthWhen; + + /* global variables */ + bool authAbort; + bool authFail; + PortState authPortStatus; + bool authStart; + bool authTimeout; + bool authSuccess; + bool eapolEap; + bool initialize; + bool keyDone; + bool keyRun; + bool keyTxEnabled; + PortTypes portControl; + bool portValid; + bool reAuthenticate; + + /* Port Timers state machine */ + /* 'bool tick' implicitly handled as registered timeout */ + + /* Authenticator PAE state machine */ + enum { AUTH_PAE_INITIALIZE, AUTH_PAE_DISCONNECTED, AUTH_PAE_CONNECTING, + AUTH_PAE_AUTHENTICATING, AUTH_PAE_AUTHENTICATED, + AUTH_PAE_ABORTING, AUTH_PAE_HELD, AUTH_PAE_FORCE_AUTH, + AUTH_PAE_FORCE_UNAUTH, AUTH_PAE_RESTART } auth_pae_state; + /* variables */ + bool eapolLogoff; + bool eapolStart; + PortTypes portMode; + unsigned int reAuthCount; + /* constants */ + unsigned int quietPeriod; /* default 60; 0..65535 */ +#define AUTH_PAE_DEFAULT_quietPeriod 60 + unsigned int reAuthMax; /* default 2 */ +#define AUTH_PAE_DEFAULT_reAuthMax 2 + /* counters */ + Counter authEntersConnecting; + Counter authEapLogoffsWhileConnecting; + Counter authEntersAuthenticating; + Counter authAuthSuccessesWhileAuthenticating; + Counter authAuthTimeoutsWhileAuthenticating; + Counter authAuthFailWhileAuthenticating; + Counter authAuthEapStartsWhileAuthenticating; + Counter authAuthEapLogoffWhileAuthenticating; + Counter authAuthReauthsWhileAuthenticated; + Counter authAuthEapStartsWhileAuthenticated; + Counter authAuthEapLogoffWhileAuthenticated; + + /* Backend Authentication state machine */ + enum { BE_AUTH_REQUEST, BE_AUTH_RESPONSE, BE_AUTH_SUCCESS, + BE_AUTH_FAIL, BE_AUTH_TIMEOUT, BE_AUTH_IDLE, BE_AUTH_INITIALIZE, + BE_AUTH_IGNORE + } be_auth_state; + /* constants */ + unsigned int serverTimeout; /* default 30; 1..X */ +#define BE_AUTH_DEFAULT_serverTimeout 30 + /* counters */ + Counter backendResponses; + Counter backendAccessChallenges; + Counter backendOtherRequestsToSupplicant; + Counter backendAuthSuccesses; + Counter backendAuthFails; + + /* Reauthentication Timer state machine */ + enum { REAUTH_TIMER_INITIALIZE, REAUTH_TIMER_REAUTHENTICATE + } reauth_timer_state; + /* constants */ + unsigned int reAuthPeriod; /* default 3600 s */ + bool reAuthEnabled; + + /* Authenticator Key Transmit state machine */ + enum { AUTH_KEY_TX_NO_KEY_TRANSMIT, AUTH_KEY_TX_KEY_TRANSMIT + } auth_key_tx_state; + + /* Key Receive state machine */ + enum { KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE } key_rx_state; + /* variables */ + bool rxKey; + + /* Controlled Directions state machine */ + enum { CTRL_DIR_FORCE_BOTH, CTRL_DIR_IN_OR_BOTH } ctrl_dir_state; + /* variables */ + ControlledDirection adminControlledDirections; + ControlledDirection operControlledDirections; + bool operEdge; + + /* Authenticator Statistics Table */ + Counter dot1xAuthEapolFramesRx; + Counter dot1xAuthEapolFramesTx; + Counter dot1xAuthEapolStartFramesRx; + Counter dot1xAuthEapolLogoffFramesRx; + Counter dot1xAuthEapolRespIdFramesRx; + Counter dot1xAuthEapolRespFramesRx; + Counter dot1xAuthEapolReqIdFramesTx; + Counter dot1xAuthEapolReqFramesTx; + Counter dot1xAuthInvalidEapolFramesRx; + Counter dot1xAuthEapLengthErrorFramesRx; + Counter dot1xAuthLastEapolFrameVersion; + + /* Other variables - not defined in IEEE 802.1X */ + u8 addr[ETH_ALEN]; /* Supplicant address */ + int flags; /* EAPOL_SM_* */ + + /* EAPOL/AAA <-> EAP full authenticator interface */ + struct eap_eapol_interface *eap_if; + + int radius_identifier; + /* TODO: check when the last messages can be released */ + struct radius_msg *last_recv_radius; + u8 last_eap_id; /* last used EAP Identifier */ + u8 *identity; + size_t identity_len; + u8 eap_type_authsrv; /* EAP type of the last EAP packet from + * Authentication server */ + u8 eap_type_supp; /* EAP type of the last EAP packet from Supplicant */ +#ifndef CONFIG_NO_RADIUS + struct wpabuf *radius_cui; /* Chargeable-User-Identity */ +#endif + + struct eap_sm *eap; + + bool initializing; /* in process of initializing state machines */ + bool changed; + + struct eapol_authenticator *eapol; + + void *sta; /* station context pointer to use in callbacks */ + + int remediation; + + u64 acct_multi_session_id; +}; + +#endif /* EAPOL_AUTH_SM_I_H */ diff --git a/components/wpa_supplicant/src/wps/wps.c b/components/wpa_supplicant/src/wps/wps.c index 9265c2436a..bb46ef3996 100644 --- a/components/wpa_supplicant/src/wps/wps.c +++ b/components/wpa_supplicant/src/wps/wps.c @@ -49,7 +49,7 @@ struct wps_data * wps_init(const struct wps_config *cfg) os_memcpy(data->mac_addr_e, cfg->wps->dev.mac_addr, ETH_ALEN); os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN); } - if (cfg->pin) { + if (cfg->pbc == 0 && cfg->pin_len) { data->dev_pw_id = cfg->dev_pw_id; data->dev_password = os_memdup(cfg->pin, cfg->pin_len); if (data->dev_password == NULL) { @@ -226,10 +226,7 @@ enum wps_process_res wps_process_msg(struct wps_data *wps, */ struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code) { - if (wps->registrar) return wps_registrar_get_msg(wps, op_code); - else - return wps_enrollee_get_msg(wps, op_code); } diff --git a/components/wpa_supplicant/src/wps/wps.h b/components/wpa_supplicant/src/wps/wps.h index aaf45cfc40..a77622fb0e 100644 --- a/components/wpa_supplicant/src/wps/wps.h +++ b/components/wpa_supplicant/src/wps/wps.h @@ -121,7 +121,7 @@ struct wps_config { /** * pin - Enrollee Device Password (%NULL for Registrar or PBC) */ - const u8 *pin; + const u8 pin[9]; /** * pin_len - Length on pin in octets @@ -132,7 +132,7 @@ struct wps_config { * pbc - Whether this is protocol run uses PBC */ int pbc; -#ifndef ESP_SUPPLICANT +#ifdef CONFIG_WPS_REGISTRAR /** * assoc_wps_ie: (Re)AssocReq WPS IE (in AP; %NULL if not AP) */ @@ -161,7 +161,6 @@ struct wps_config { * struct wpa_context::psk. */ int use_psk_key; - #endif /** * dev_pw_id - Device Password ID for Enrollee when PIN is used @@ -228,7 +227,6 @@ enum wps_process_res { * event (e.g., UPnP message from an external Registrar) */ WPS_PENDING, - WPS_IGNORE, /* snake, ignore the re-packge */ WPS_FRAGMENT /* Tim, send wsc fragment ack */ }; diff --git a/components/wpa_supplicant/src/wps/wps_enrollee.c b/components/wpa_supplicant/src/wps/wps_enrollee.c index 59e128d5c3..6f53d907c6 100644 --- a/components/wpa_supplicant/src/wps/wps_enrollee.c +++ b/components/wpa_supplicant/src/wps/wps_enrollee.c @@ -152,11 +152,7 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) wps_build_wps_state(wps, msg) || wps_build_device_attrs(&wps->wps->dev, msg) || wps_build_rf_bands(&wps->wps->dev, msg, -#ifdef ESP_SUPPLICANT - wps->wps->dev.rf_bands) || -#else wps->wps->rf_band_cb(wps->wps->cb_ctx)) || -#endif wps_build_assoc_state(wps, msg) || wps_build_dev_password_id(msg, wps->dev_pw_id) || wps_build_config_error(msg, WPS_CFG_NO_ERROR) || @@ -245,6 +241,7 @@ static struct wpabuf * wps_build_m5(struct wps_data *wps) } +#ifndef ESP_SUPPLICANT static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg) { wpa_printf(MSG_DEBUG, "WPS: * SSID"); @@ -351,13 +348,12 @@ static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg) wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN); return 0; } - +#endif static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain) { -#ifdef DEBUG_PRINT +#ifndef ESP_SUPPLICANT const u8 *start, *end; -#endif int ret; if (wps->wps->ap_settings) { @@ -368,22 +364,21 @@ static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain) } wpa_printf(MSG_DEBUG, "WPS: * AP Settings based on current configuration"); -#ifdef DEBUG_PRINT start = wpabuf_put(plain, 0); -#endif ret = wps_build_cred_ssid(wps, plain) || wps_build_cred_mac_addr(wps, plain) || wps_build_cred_auth_type(wps, plain) || wps_build_cred_encr_type(wps, plain) || wps_build_cred_network_key(wps, plain); -#ifdef DEBUG_PRINT end = wpabuf_put(plain, 0); wpa_hexdump_key(MSG_DEBUG, "WPS: Plaintext AP Settings", start, end - start); -#endif return ret; +#else + return 0; +#endif } diff --git a/components/wpa_supplicant/src/wps/wps_registrar.c b/components/wpa_supplicant/src/wps/wps_registrar.c index 14c9072a05..d16210b830 100644 --- a/components/wpa_supplicant/src/wps/wps_registrar.c +++ b/components/wpa_supplicant/src/wps/wps_registrar.c @@ -10,6 +10,7 @@ #include "utils/common.h" #include "utils/base64.h" +#include "utils/eloop.h" #include "utils/uuid.h" #include "utils/list.h" #include "crypto/crypto.h" @@ -699,6 +700,7 @@ wps_registrar_init(struct wps_context *wps, reg->dualband = cfg->dualband; reg->force_per_enrollee_psk = cfg->force_per_enrollee_psk; +#ifndef ESP_SUPPLICANT if (cfg->multi_ap_backhaul_ssid) { os_memcpy(reg->multi_ap_backhaul_ssid, cfg->multi_ap_backhaul_ssid, @@ -714,6 +716,7 @@ wps_registrar_init(struct wps_context *wps, reg->multi_ap_backhaul_network_key_len = cfg->multi_ap_backhaul_network_key_len; } +#endif if (wps_set_ie(reg)) { wps_registrar_deinit(reg); @@ -1291,6 +1294,7 @@ static void wps_cb_set_sel_reg(struct wps_registrar *reg) } +#ifndef ESP_SUPPLICANT static int wps_cp_lookup_pskfile(struct wps_registrar *reg, const u8 *mac_addr, const u8 **psk) { @@ -1298,7 +1302,7 @@ static int wps_cp_lookup_pskfile(struct wps_registrar *reg, const u8 *mac_addr, return 0; return reg->lookup_pskfile_cb(reg->cb_ctx, mac_addr, psk); } - +#endif static int wps_set_ie(struct wps_registrar *reg) { @@ -1619,9 +1623,11 @@ int wps_build_credential_wrap(struct wpabuf *msg, int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) { struct wpabuf *cred; +#ifndef ESP_SUPPLICANT struct wps_registrar *reg = wps->wps->registrar; const u8 *pskfile_psk; char hex[65]; +#endif if (wps->wps->registrar->skip_cred_build) goto skip_cred_build; @@ -1631,6 +1637,7 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) os_memcpy(&wps->cred, wps->use_cred, sizeof(wps->cred)); goto use_provided; } +#ifndef ESP_SUPPLICANT os_memset(&wps->cred, 0, sizeof(wps->cred)); if (wps->peer_dev.multi_ap_ext == MULTI_AP_BACKHAUL_STA && @@ -1741,12 +1748,23 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) wps->new_psk, wps->new_psk_len); os_memcpy(wps->cred.key, wps->new_psk, wps->new_psk_len); wps->cred.key_len = wps->new_psk_len; - } else if (wps->use_psk_key && wps->wps->psk_set) { - char hex[65] = {0}; - wpa_printf(MSG_DEBUG, "WPS: Use PSK format for Network Key"); - os_memcpy(wps->cred.key, hex, 32 * 2); - wps->cred.key_len = 32 * 2; - } else if (wps->wps->network_key) { + } else if (wps_cp_lookup_pskfile(reg, wps->mac_addr_e, &pskfile_psk)) { + wpa_hexdump_key(MSG_DEBUG, "WPS: Use PSK from wpa_psk_file", + pskfile_psk, PMK_LEN); + wpa_snprintf_hex(hex, sizeof(hex), pskfile_psk, PMK_LEN); + os_memcpy(wps->cred.key, hex, PMK_LEN * 2); + wps->cred.key_len = PMK_LEN * 2; + } else if (!wps->wps->registrar->force_per_enrollee_psk && + wps->use_psk_key && wps->wps->psk_set) { + wpa_printf(MSG_DEBUG, "WPS: Use PSK format for Network Key"); + wpa_snprintf_hex(hex, sizeof(hex), wps->wps->psk, PMK_LEN); + os_memcpy(wps->cred.key, hex, PMK_LEN * 2); + wps->cred.key_len = PMK_LEN * 2; + } else + if ((!wps->wps->registrar->force_per_enrollee_psk || + wps->wps->use_passphrase) && wps->wps->network_key) { + wpa_printf(MSG_DEBUG, + "WPS: Use passphrase format for Network key"); os_memcpy(wps->cred.key, wps->wps->network_key, wps->wps->network_key_len); wps->cred.key_len = wps->wps->network_key_len; @@ -1772,6 +1790,7 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) os_memcpy(wps->cred.key, hex, wps->new_psk_len * 2); wps->cred.key_len = wps->new_psk_len * 2; } +#endif use_provided: #ifdef CONFIG_WPS_TESTING @@ -1836,6 +1855,7 @@ static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *msg) } +#ifndef ESP_SUPPLICANT static struct wpabuf * wps_build_ap_cred(struct wps_data *wps) { struct wpabuf *msg, *plain; @@ -1863,6 +1883,7 @@ static struct wpabuf * wps_build_ap_cred(struct wps_data *wps) return msg; } +#endif static struct wpabuf * wps_build_m2(struct wps_data *wps) @@ -2563,32 +2584,22 @@ static int wps_process_wps_state(struct wps_data *wps, const u8 *state) static int wps_process_assoc_state(struct wps_data *wps, const u8 *assoc) { - u16 a; - if (assoc == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Association State received"); return -1; } - a = WPA_GET_BE16(assoc); - wpa_printf(MSG_DEBUG, "WPS: Enrollee Association State %d", a); - return 0; } static int wps_process_config_error(struct wps_data *wps, const u8 *err) { - u16 e; - if (err == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Configuration Error received"); return -1; } - e = WPA_GET_BE16(err); - wpa_printf(MSG_DEBUG, "WPS: Enrollee Configuration Error %d", e); - return 0; } @@ -2863,6 +2874,7 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps, } +#ifndef ESP_SUPPLICANT static void wps_sta_cred_cb(struct wps_data *wps) { /* @@ -2949,8 +2961,15 @@ static int wps_process_ap_settings_r(struct wps_data *wps, return 1; } + return 0; } - +#else +static int wps_process_ap_settings_r(struct wps_data *wps, + struct wps_parse_attr *attr) +{ + return 0; +} +#endif static enum wps_process_res wps_process_m7(struct wps_data *wps, const struct wpabuf *msg, @@ -3301,6 +3320,7 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, } wpa_printf(MSG_DEBUG, "WPS: Negotiation completed successfully"); +#ifndef ESP_SUPPLICANT wps_device_store(wps->wps->registrar, &wps->peer_dev, wps->uuid_e); @@ -3349,6 +3369,7 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, wps->new_psk = NULL; } +#endif wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e, wps->dev_password, wps->dev_password_len);