diff --git a/components/wpa_supplicant/esp_supplicant/include/esp_eap_client.h b/components/wpa_supplicant/esp_supplicant/include/esp_eap_client.h index b18290186d..3827663e08 100644 --- a/components/wpa_supplicant/esp_supplicant/include/esp_eap_client.h +++ b/components/wpa_supplicant/esp_supplicant/include/esp_eap_client.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -319,6 +319,22 @@ esp_err_t esp_eap_client_set_fast_params(esp_eap_fast_config config); */ esp_err_t esp_eap_client_use_default_cert_bundle(bool use_default_bundle); +/** + * This function sets the expected domain name for validating the certificate's subject name. + * If the provided domain name does not match the certificate's subject name, validation will fail. + * + * @attention 1. The `domain_name` should be a NULL-terminated string. + * + * @param[in] domain_name The expected domain name. Pass `NULL` to clear the domain matching. + * + * @return + * - ESP_OK: The domain match was set successfully. + * - ESP_ERR_INVALID_ARG: Invalid argument (length > 255). + * - ESP_ERR_NO_MEM: Memory allocation failure. + * - ESP_ERR_NOT_SUPPORTED: Feature not supported. + */ +esp_err_t esp_eap_client_set_domain_name(const char *domain_name); + #ifdef __cplusplus } #endif diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/tls_mbedtls.c b/components/wpa_supplicant/esp_supplicant/src/crypto/tls_mbedtls.c index 43387e0cc3..803bcc74af 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/tls_mbedtls.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/tls_mbedtls.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -520,18 +520,24 @@ static int set_client_config(const struct tls_connection_params *cfg, tls_contex } } - /* Usages of default ciphersuites can take a lot of time on low end device - * and can cause watchdog. Enabling the ciphers which are secured enough - * but doesn't take that much processing power */ + /* The use of default ciphersuites may take a lot of time on low-end devices + * and may trigger the watchdog timer. Enable ciphers that are secure enough + * but require less processing power. */ tls_set_ciphersuite(cfg, tls); #ifdef CONFIG_ESP_WIFI_DISABLE_KEY_USAGE_CHECK mbedtls_ssl_set_verify(&tls->ssl, tls_disable_key_usages, NULL); #endif /*CONFIG_ESP_WIFI_DISABLE_KEY_USAGE_CHECK*/ + ret = mbedtls_ssl_set_hostname(&tls->ssl, cfg->domain_match); + if (ret != 0) { + wpa_printf(MSG_ERROR, "Failed to set hostname"); + return ret; + } #ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE if (cfg->flags & TLS_CONN_USE_DEFAULT_CERT_BUNDLE) { wpa_printf(MSG_INFO, "Using default cert bundle"); + mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED); if (esp_crt_bundle_attach_fn) { ret = (*esp_crt_bundle_attach_fn)(&tls->conf); } diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_eap_client.c b/components/wpa_supplicant/esp_supplicant/src/esp_eap_client.c index aedc3f1652..94d0b0bd5f 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_eap_client.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_eap_client.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -38,6 +38,7 @@ #include "esp_wpas_glue.h" #include "esp_eap_client_i.h" #include "esp_eap_client.h" +#include "eloop.h" #define WPA2_VERSION "v2.0" @@ -63,6 +64,7 @@ static void eap_peer_sm_deinit(void); static int eap_sm_rx_eapol_internal(u8 *src_addr, u8 *buf, u32 len, uint8_t *bssid); static int wpa2_start_eapol_internal(void); int wpa2_post(uint32_t sig, uint32_t par); +extern bool g_wpa_config_changed; #ifdef USE_WPA2_TASK #define WPA2_TASK_PRIORITY 7 @@ -73,6 +75,11 @@ static void *s_wpa2_api_lock = NULL; static void *s_wifi_wpa2_sync_sem = NULL; static bool s_disable_time_check = true; +static void config_changed_handler(void *ctx, void *data) +{ + g_wpa_config_changed = true; +} + static void wpa2_api_lock(void) { if (s_wpa2_api_lock == NULL) { @@ -812,6 +819,7 @@ static esp_err_t esp_client_enable_fn(void *arg) wpa_printf(MSG_ERROR, "Register EAP Peer methods Failure"); } #endif + g_wpa_config_changed = true; return ESP_OK; } @@ -861,6 +869,7 @@ static esp_err_t eap_client_disable_fn(void *param) #endif sm->wpa_sm_eap_disable = NULL; + g_wpa_config_changed = true; return ESP_OK; } @@ -908,6 +917,7 @@ esp_err_t esp_eap_client_set_certificate_and_key(const unsigned char *client_cer g_wpa_private_key_passwd = private_key_passwd; g_wpa_private_key_passwd_len = private_key_passwd_len; } + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_OK; } @@ -923,6 +933,7 @@ void esp_eap_client_clear_certificate_and_key(void) os_free(g_wpa_pac_file); g_wpa_pac_file = NULL; g_wpa_pac_file_len = 0; + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); } esp_err_t esp_eap_client_set_ca_cert(const unsigned char *ca_cert, int ca_cert_len) @@ -932,6 +943,9 @@ esp_err_t esp_eap_client_set_ca_cert(const unsigned char *ca_cert, int ca_cert_l g_wpa_ca_cert_len = ca_cert_len; } + /* CA certs Set/updated, flushing current PMK cache */ + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); + return ESP_OK; } @@ -939,6 +953,7 @@ void esp_eap_client_clear_ca_cert(void) { g_wpa_ca_cert = NULL; g_wpa_ca_cert_len = 0; + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); } #define ANONYMOUS_ID_LEN_MAX 128 @@ -955,23 +970,27 @@ esp_err_t esp_eap_client_set_identity(const unsigned char *identity, int len) g_wpa_anonymous_identity = (u8 *)os_zalloc(len); if (g_wpa_anonymous_identity == NULL) { + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_ERR_NO_MEM; } os_memcpy(g_wpa_anonymous_identity, identity, len); g_wpa_anonymous_identity_len = len; + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_OK; } void esp_eap_client_clear_identity(void) { - if (g_wpa_anonymous_identity) { - os_free(g_wpa_anonymous_identity); + if (!g_wpa_anonymous_identity) { + return; } + os_free(g_wpa_anonymous_identity); g_wpa_anonymous_identity = NULL; g_wpa_anonymous_identity_len = 0; + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); } #define USERNAME_LEN_MAX 128 @@ -988,11 +1007,13 @@ esp_err_t esp_eap_client_set_username(const unsigned char *username, int len) g_wpa_username = (u8 *)os_zalloc(len); if (g_wpa_username == NULL) { + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_ERR_NO_MEM; } os_memcpy(g_wpa_username, username, len); g_wpa_username_len = len; + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_OK; } @@ -1005,6 +1026,7 @@ void esp_eap_client_clear_username(void) g_wpa_username = NULL; g_wpa_username_len = 0; + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); } esp_err_t esp_eap_client_set_password(const unsigned char *password, int len) @@ -1020,11 +1042,13 @@ esp_err_t esp_eap_client_set_password(const unsigned char *password, int len) g_wpa_password = (u8 *)os_zalloc(len); if (g_wpa_password == NULL) { + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_ERR_NO_MEM; } os_memcpy(g_wpa_password, password, len); g_wpa_password_len = len; + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_OK; } @@ -1036,6 +1060,7 @@ void esp_eap_client_clear_password(void) } g_wpa_password = NULL; g_wpa_password_len = 0; + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); } esp_err_t esp_eap_client_set_new_password(const unsigned char *new_password, int len) @@ -1051,11 +1076,13 @@ esp_err_t esp_eap_client_set_new_password(const unsigned char *new_password, int g_wpa_new_password = (u8 *)os_zalloc(len); if (g_wpa_new_password == NULL) { + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_ERR_NO_MEM; } os_memcpy(g_wpa_new_password, new_password, len); g_wpa_password_len = len; + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_OK; } @@ -1067,11 +1094,13 @@ void esp_eap_client_clear_new_password(void) } g_wpa_new_password = NULL; g_wpa_new_password_len = 0; + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); } esp_err_t esp_eap_client_set_disable_time_check(bool disable) { s_disable_time_check = disable; + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_OK; } @@ -1108,6 +1137,7 @@ esp_err_t esp_eap_client_set_ttls_phase2_method(esp_eap_ttls_phase2_types type) g_wpa_ttls_phase2_type = "auth=MSCHAPV2"; break; } + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_OK; } @@ -1115,6 +1145,7 @@ esp_err_t esp_eap_client_set_suiteb_192bit_certification(bool enable) { #ifdef CONFIG_SUITEB192 g_wpa_suiteb_certification = enable; + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_OK; #else return ESP_FAIL; @@ -1133,6 +1164,7 @@ esp_err_t esp_eap_client_set_pac_file(const unsigned char *pac_file, int pac_fil } else { // The file contains pac data g_wpa_pac_file = (u8 *)os_zalloc(pac_file_len); if (g_wpa_pac_file == NULL) { + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_ERR_NO_MEM; } os_memcpy(g_wpa_pac_file, pac_file, pac_file_len); @@ -1141,6 +1173,7 @@ esp_err_t esp_eap_client_set_pac_file(const unsigned char *pac_file, int pac_fil } else { return ESP_FAIL; } + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_OK; } @@ -1170,9 +1203,11 @@ esp_err_t esp_eap_client_set_fast_params(esp_eap_fast_config config) } g_wpa_phase1_options = (char *)os_zalloc(sizeof(config_for_supplicant)); if (g_wpa_phase1_options == NULL) { + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_ERR_NO_MEM; } os_memcpy(g_wpa_phase1_options, &config_for_supplicant, sizeof(config_for_supplicant)); + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_OK; } @@ -1186,8 +1221,43 @@ esp_err_t esp_eap_client_use_default_cert_bundle(bool use_default_bundle) } else { esp_crt_bundle_attach_fn = NULL; } + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); return ESP_OK; #else return ESP_FAIL; #endif } + +#define MAX_DOMAIN_MATCH_LEN 255 /* Maximum host name defined in RFC 1035 */ +esp_err_t esp_eap_client_set_domain_name(const char *domain_name) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + return ESP_ERR_NOT_SUPPORTED; +#else + int len = domain_name ? os_strnlen(domain_name, MAX_DOMAIN_MATCH_LEN + 1) : 0; + if (len > MAX_DOMAIN_MATCH_LEN) { + return ESP_ERR_INVALID_ARG; + } + if (g_wpa_domain_match && domain_name && os_strcmp(g_wpa_domain_match, domain_name) == 0) { + return ESP_OK; + } + if (g_wpa_domain_match) { + os_free(g_wpa_domain_match); + g_wpa_domain_match = NULL; + } + + if (!domain_name) { + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); + return ESP_OK; + } + g_wpa_domain_match = os_strdup(domain_name); + if (!g_wpa_domain_match) { + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); + return ESP_ERR_NO_MEM; + } + + eloop_register_timeout(0, 0, config_changed_handler, NULL, NULL); + + return ESP_OK; +#endif +} 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 21b9a60049..24cbc34a21 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c @@ -50,6 +50,7 @@ bool g_wpa_pmk_caching_disabled = 0; const wifi_osi_funcs_t *wifi_funcs; struct wpa_funcs *wpa_cb; +bool g_wpa_config_changed; 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) @@ -226,11 +227,22 @@ int dpp_connect(uint8_t *bssid, bool pdr_done) } #endif +static void wpa_config_reload(void) +{ + struct wpa_sm *sm = &gWpaSm; + wpa_sm_pmksa_cache_flush(sm, NULL); +} + int wpa_sta_connect(uint8_t *bssid) { /* use this API to set AP specific IEs during connection */ int ret = 0; ret = wpa_config_profile(bssid); + + if (g_wpa_config_changed) { + wpa_config_reload(); + g_wpa_config_changed = false; + } if (ret == 0) { ret = wpa_config_bss(bssid); if (ret) { @@ -444,12 +456,6 @@ fail: } #endif -static void wpa_config_reload(void) -{ - struct wpa_sm *sm = &gWpaSm; - wpa_sm_pmksa_cache_flush(sm, NULL); -} - int esp_supplicant_init(void) { int ret = ESP_OK; diff --git a/components/wpa_supplicant/port/include/os.h b/components/wpa_supplicant/port/include/os.h index 2c6e594049..3ed465b3ae 100644 --- a/components/wpa_supplicant/port/include/os.h +++ b/components/wpa_supplicant/port/include/os.h @@ -260,6 +260,9 @@ char * ets_strdup(const char *s); #ifndef os_strlen #define os_strlen(s) strlen(s) #endif +#ifndef os_strnlen +#define os_strnlen(s, n) strnlen((s), (n)) +#endif #ifndef os_strcasecmp #ifdef _MSC_VER #define os_strcasecmp(s1, s2) _stricmp((s1), (s2)) diff --git a/components/wpa_supplicant/src/eap_peer/eap.c b/components/wpa_supplicant/src/eap_peer/eap.c index 2658ad211b..c06268c608 100644 --- a/components/wpa_supplicant/src/eap_peer/eap.c +++ b/components/wpa_supplicant/src/eap_peer/eap.c @@ -67,6 +67,9 @@ bool g_wpa_suiteb_certification; bool g_wpa_default_cert_bundle; int (*esp_crt_bundle_attach_fn)(void *conf); #endif +#ifndef CONFIG_TLS_INTERNAL_CLIENT +char *g_wpa_domain_match; +#endif void eap_peer_config_deinit(struct eap_sm *sm); void eap_peer_blob_deinit(struct eap_sm *sm); @@ -529,7 +532,9 @@ int eap_peer_config_init( sm->config.identity = NULL; sm->config.password = NULL; sm->config.new_password = NULL; - +#ifndef CONFIG_TLS_INTERNAL_CLIENT + sm->config.domain_match = g_wpa_domain_match; +#endif sm->config.private_key_passwd = private_key_passwd; sm->config.client_cert = (u8 *)sm->blob[0].name; sm->config.private_key = (u8 *)sm->blob[1].name; diff --git a/components/wpa_supplicant/src/eap_peer/eap.h b/components/wpa_supplicant/src/eap_peer/eap.h index 0651bd5ec5..93d623d1cc 100644 --- a/components/wpa_supplicant/src/eap_peer/eap.h +++ b/components/wpa_supplicant/src/eap_peer/eap.h @@ -49,6 +49,10 @@ extern bool g_wpa_suiteb_certification; extern bool g_wpa_default_cert_bundle; extern int (*esp_crt_bundle_attach_fn)(void *conf); +#ifndef CONFIG_TLS_INTERNAL_CLIENT +extern char *g_wpa_domain_match; +#endif + const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len); void eap_deinit_prev_method(struct eap_sm *sm, const char *txt); struct wpabuf * eap_sm_build_nak(struct eap_sm *sm, EapType type, u8 id); diff --git a/components/wpa_supplicant/src/eap_peer/eap_config.h b/components/wpa_supplicant/src/eap_peer/eap_config.h index 60eda8c954..6dee56c7ac 100644 --- a/components/wpa_supplicant/src/eap_peer/eap_config.h +++ b/components/wpa_supplicant/src/eap_peer/eap_config.h @@ -159,6 +159,21 @@ struct eap_peer_config { */ const u8 *private_key_passwd; + /** + * domain_match - Constraint for server domain name + * + * If set, this FQDN is used as a full match requirement for the + * server certificate in SubjectAltName dNSName element(s). If a + * matching dNSName is found, this constraint is met. If no dNSName + * values are present, this constraint is matched against SubjectName CN + * using same full match comparison. This behavior is similar to + * domain_suffix_match, but has the requirement of a full match, i.e., + * no subdomains or wildcard matches are allowed. Case-insensitive + * comparison is used, so "Example.com" matches "example.com", but would + * not match "test.Example.com". + */ + char *domain_match; + /** * Phase 2 */ diff --git a/components/wpa_supplicant/src/eap_peer/eap_tls_common.c b/components/wpa_supplicant/src/eap_peer/eap_tls_common.c index d126df8213..d32844d293 100644 --- a/components/wpa_supplicant/src/eap_peer/eap_tls_common.c +++ b/components/wpa_supplicant/src/eap_peer/eap_tls_common.c @@ -74,6 +74,7 @@ static void eap_tls_params_from_conf1(struct tls_connection_params *params, params->client_cert = (char *) config->client_cert; params->private_key = (char *) config->private_key; params->private_key_passwd = (char *) config->private_key_passwd; + params->domain_match = config->domain_match; eap_tls_params_flags(params, config->phase1); if (wifi_sta_get_enterprise_disable_time_check()) params->flags |= TLS_CONN_DISABLE_TIME_CHECKS; diff --git a/examples/wifi/wifi_enterprise/main/Kconfig.projbuild b/examples/wifi/wifi_enterprise/main/Kconfig.projbuild index ecee60be3e..0e5e606b33 100644 --- a/examples/wifi/wifi_enterprise/main/Kconfig.projbuild +++ b/examples/wifi/wifi_enterprise/main/Kconfig.projbuild @@ -99,4 +99,19 @@ menu "Example Configuration" default n help Use default CA certificate bundle for WiFi enterprise connection + + config EXAMPLE_VALIDATE_SERVER_CERT_DOMAIN + bool "Enable server certificate domain validation" + depends on EXAMPLE_VALIDATE_SERVER_CERT + default n + help + Enable validation of the server certificate's domain name. + + config EXAMPLE_SERVER_CERT_DOMAIN + string "Expected server certificate domain" + depends on EXAMPLE_VALIDATE_SERVER_CERT_DOMAIN + default "espressif.com" + help + Specify the expected domain name for the server certificate. + The connection will be accepted only if the server certificate matches this domain. endmenu diff --git a/examples/wifi/wifi_enterprise/main/wifi_enterprise_main.c b/examples/wifi/wifi_enterprise/main/wifi_enterprise_main.c index aebd169395..a1ebb65e1d 100644 --- a/examples/wifi/wifi_enterprise/main/wifi_enterprise_main.c +++ b/examples/wifi/wifi_enterprise/main/wifi_enterprise_main.c @@ -1,6 +1,6 @@ /* * SPDX-FileCopyrightText: 2006-2016 ARM Limited - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -33,6 +33,7 @@ #define EXAMPLE_EAP_ID CONFIG_EXAMPLE_EAP_ID #define EXAMPLE_EAP_USERNAME CONFIG_EXAMPLE_EAP_USERNAME #define EXAMPLE_EAP_PASSWORD CONFIG_EXAMPLE_EAP_PASSWORD +#define EXAMPLE_SERVER_CERT_DOMAIN CONFIG_EXAMPLE_SERVER_CERT_DOMAIN /* FreeRTOS event group to signal when we are connected & ready to make a request */ static EventGroupHandle_t wifi_event_group; @@ -155,6 +156,9 @@ static void initialise_wifi(void) #endif #ifdef CONFIG_EXAMPLE_USE_DEFAULT_CERT_BUNDLE ESP_ERROR_CHECK(esp_eap_client_use_default_cert_bundle(true)); +#endif +#ifdef CONFIG_EXAMPLE_VALIDATE_SERVER_CERT_DOMAIN + ESP_ERROR_CHECK(esp_eap_client_set_domain_name(EXAMPLE_SERVER_CERT_DOMAIN)); #endif ESP_ERROR_CHECK(esp_wifi_sta_enterprise_enable()); ESP_ERROR_CHECK(esp_wifi_start());