/* * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include "esp_err.h" #include "utils/includes.h" #include "utils/common.h" #include "utils/wpa_debug.h" #include "common/wpa_ctrl.h" #include "common/eapol_common.h" #include "common/ieee802_11_defs.h" #include "utils/state_machine.h" #include "rsn_supp/wpa.h" #include "crypto/crypto.h" #include "utils/ext_password.h" #include "crypto/tls.h" #include "eap_peer/eap_i.h" #include "eap_peer/eap_config.h" #include "eap_peer/eap.h" #include "eap_peer/eap_tls.h" #ifdef EAP_PEER_METHOD #include "eap_peer/eap_methods.h" #endif #include "esp_wifi_driver.h" #include "esp_private/wifi.h" #include "esp_wpa_err.h" #ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE #include "esp_crt_bundle.h" #endif #include "esp_wpas_glue.h" #include "esp_eap_client_i.h" #include "esp_eap_client.h" #define WPA2_VERSION "v2.0" #define DATA_MUTEX_TAKE() os_mutex_lock(s_wpa2_data_lock) #define DATA_MUTEX_GIVE() os_mutex_unlock(s_wpa2_data_lock) //length of the string "fast_provisioning={0/1/2} " #define FAST_PROVISIONING_CONFIG_STR_LEN 20 //length of the string "fast_max_pac_list_len=(int < 100) " #define FAST_MAX_PAC_LIST_CONFIG_STR_LEN 25 //length of the string "fast_pac_format=binary" #define FAST_PAC_FORMAT_STR_LEN 22 //Total #define PHASE1_PARAM_STRING_LEN FAST_PROVISIONING_CONFIG_STR_LEN + FAST_MAX_PAC_LIST_CONFIG_STR_LEN + FAST_PAC_FORMAT_STR_LEN static void *s_wpa2_data_lock = NULL; static struct eap_sm *gEapSm = NULL; static int eap_peer_sm_init(void); 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); #ifdef USE_WPA2_TASK #define WPA2_TASK_PRIORITY 7 static void *s_wpa2_task_hdl = NULL; static void *s_wpa2_queue = NULL; static wpa2_state_t s_wpa2_state = WPA2_STATE_DISABLED; static void *s_wpa2_api_lock = NULL; static void *s_wifi_wpa2_sync_sem = NULL; static bool s_disable_time_check = true; static void wpa2_api_lock(void) { if (s_wpa2_api_lock == NULL) { s_wpa2_api_lock = os_recursive_mutex_create(); if (!s_wpa2_api_lock) { wpa_printf(MSG_ERROR, "EAP: failed to create EAP api lock"); return; } } os_mutex_lock(s_wpa2_api_lock); } static void wpa2_api_unlock(void) { if (s_wpa2_api_lock) { os_mutex_unlock(s_wpa2_api_lock); } } static bool inline wpa2_is_enabled(void) { return (s_wpa2_state == WPA2_STATE_ENABLED); } static bool inline wpa2_is_disabled(void) { return (s_wpa2_state == WPA2_STATE_DISABLED); } static void inline wpa2_set_state(wpa2_state_t state) { s_wpa2_state = state; } static void wpa2_set_eap_state(wpa2_ent_eap_state_t state) { if (!gEapSm) { return; } gEapSm->finish_state = state; esp_wifi_set_wpa2_ent_state_internal(state); } wpa2_ent_eap_state_t eap_client_get_eap_state(void) { if (!gEapSm) { return WPA2_ENT_EAP_STATE_NOT_START; } return gEapSm->finish_state; } static inline void wpa2_task_delete(void *arg) { void *my_task_hdl = os_task_get_current_task(); int ret = ESP_OK; if (my_task_hdl == s_wpa2_task_hdl) { wpa_printf(MSG_ERROR, "EAP: should never call task delete api in eap task context"); return; } ret = wpa2_post(SIG_WPA2_TASK_DEL, 0); if (ESP_OK != ret) { wpa_printf(MSG_ERROR, "EAP: failed to post task delete event, ret=%d", ret); return; } } #define WPA_ADDR_LEN 6 struct wpa2_rx_param { uint8_t *bssid; u8 sa[WPA_ADDR_LEN]; u8 *buf; int len; STAILQ_ENTRY(wpa2_rx_param) bqentry; }; static STAILQ_HEAD(, wpa2_rx_param) s_wpa2_rxq; static void wpa2_rxq_init(void) { DATA_MUTEX_TAKE(); STAILQ_INIT(&s_wpa2_rxq); DATA_MUTEX_GIVE(); } static void wpa2_rxq_enqueue(struct wpa2_rx_param *param) { DATA_MUTEX_TAKE(); STAILQ_INSERT_TAIL(&s_wpa2_rxq,param, bqentry); DATA_MUTEX_GIVE(); } static struct wpa2_rx_param * wpa2_rxq_dequeue(void) { struct wpa2_rx_param *param = NULL; DATA_MUTEX_TAKE(); if ((param = STAILQ_FIRST(&s_wpa2_rxq)) != NULL) { STAILQ_REMOVE_HEAD(&s_wpa2_rxq, bqentry); STAILQ_NEXT(param,bqentry) = NULL; } DATA_MUTEX_GIVE(); return param; } static void wpa2_rxq_deinit(void) { struct wpa2_rx_param *param = NULL; DATA_MUTEX_TAKE(); while ((param = STAILQ_FIRST(&s_wpa2_rxq)) != NULL) { STAILQ_REMOVE_HEAD(&s_wpa2_rxq, bqentry); STAILQ_NEXT(param,bqentry) = NULL; os_free(param->buf); os_free(param); } DATA_MUTEX_GIVE(); } void wpa2_task(void *pvParameters ) { ETSEvent *e; struct eap_sm *sm = gEapSm; bool task_del = false; if (!sm) { return; } for (;;) { if ( TRUE == os_queue_recv(s_wpa2_queue, &e, OS_BLOCK) ) { if (e->sig < SIG_WPA2_MAX) { DATA_MUTEX_TAKE(); if(sm->wpa2_sig_cnt[e->sig]) { sm->wpa2_sig_cnt[e->sig]--; } else { wpa_printf(MSG_ERROR, "wpa2_task: invalid sig cnt, sig=%" PRId32 " cnt=%d", e->sig, sm->wpa2_sig_cnt[e->sig]); } DATA_MUTEX_GIVE(); } switch (e->sig) { case SIG_WPA2_TASK_DEL: task_del = true; break; case SIG_WPA2_START: wpa2_start_eapol_internal(); break; case SIG_WPA2_RX: { struct wpa2_rx_param *param = NULL; while ((param = wpa2_rxq_dequeue()) != NULL){ eap_sm_rx_eapol_internal(param->sa, param->buf, param->len, param->bssid); os_free(param->buf); os_free(param); } break; } default: break; } os_free(e); } if (task_del) { break; } else { if (s_wifi_wpa2_sync_sem) { wpa_printf(MSG_DEBUG, "EAP: wifi->EAP api completed sig(%" PRId32 ")", e->sig); os_semphr_give(s_wifi_wpa2_sync_sem); } else { wpa_printf(MSG_ERROR, "EAP: null wifi->EAP sync sem"); } } } wpa_printf(MSG_DEBUG, "EAP: queue deleted"); os_queue_delete(s_wpa2_queue); wpa_printf(MSG_DEBUG, "EAP: task deleted"); s_wpa2_queue = NULL; if (s_wifi_wpa2_sync_sem) { wpa_printf(MSG_DEBUG, "EAP: wifi->EAP api completed sig(%" PRId32 ")", e->sig); os_semphr_give(s_wifi_wpa2_sync_sem); } else { wpa_printf(MSG_ERROR, "EAP: null wifi->EAP sync sem"); } /* At this point, we completed */ os_task_delete(NULL); } int wpa2_post(uint32_t sig, uint32_t par) { struct eap_sm *sm = gEapSm; if (!sm) { return ESP_FAIL; } DATA_MUTEX_TAKE(); if (sm->wpa2_sig_cnt[sig]) { DATA_MUTEX_GIVE(); return ESP_OK; } else { ETSEvent *evt = (ETSEvent *)os_malloc(sizeof(ETSEvent)); if (evt == NULL) { wpa_printf(MSG_ERROR, "EAP: E N M"); DATA_MUTEX_GIVE(); return ESP_FAIL; } sm->wpa2_sig_cnt[sig]++; DATA_MUTEX_GIVE(); evt->sig = sig; evt->par = par; if (os_queue_send(s_wpa2_queue, &evt, os_task_ms_to_tick(10)) != TRUE) { wpa_printf(MSG_ERROR, "EAP: Q S E"); return ESP_FAIL; } else { if (s_wifi_wpa2_sync_sem) { os_semphr_take(s_wifi_wpa2_sync_sem, OS_BLOCK); wpa_printf(MSG_DEBUG, "EAP: EAP api return, sm->state(%d)", sm->finish_state); } else { wpa_printf(MSG_ERROR, "EAP: null wifi->EAP sync sem"); } } } return ESP_OK; } #endif /* USE_WPA2_TASK */ int eap_sm_send_eapol(struct eap_sm *sm, struct wpabuf *resp) { size_t outlen; int ret; u8 *outbuf = NULL; u8 bssid[6]; ret = esp_wifi_get_assoc_bssid_internal(bssid); if (ret != 0) { wpa_printf(MSG_DEBUG, "bssid is empty"); return WPA_ERR_INVALID_BSSID; } outbuf = wpa_alloc_eapol(sm, IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head_u8(resp), wpabuf_len(resp), &outlen, NULL); if (!outbuf) { return ESP_ERR_NO_MEM; } ret = wpa_ether_send(sm, bssid, ETH_P_EAPOL, outbuf, outlen); wpa_free_eapol(outbuf); if (ret) { return ESP_FAIL; } return ESP_OK; } int eap_sm_process_request(struct eap_sm *sm, struct wpabuf *reqData) { size_t plen; u32 reqVendor, reqVendorMethod; u8 type, *pos; struct eap_hdr *ehdr; const struct eap_method *m = NULL; struct wpabuf *resp = NULL; struct eap_method_ret m_res; int ret = 0; if (reqData == NULL || wpabuf_len(reqData) < sizeof(*ehdr)) { return ESP_ERR_INVALID_ARG; } ehdr = (struct eap_hdr *)wpabuf_head(reqData); plen = be_to_host16(ehdr->length); if (plen > wpabuf_len(reqData)) { return ESP_FAIL; } if (ehdr->identifier == sm->current_identifier && sm->lastRespData != NULL) { /*Retransmit*/ resp = sm->lastRespData; goto send_resp; } sm->current_identifier = ehdr->identifier; pos = (u8 *)(ehdr + 1); type = *pos++; if (type == EAP_TYPE_IDENTITY) { resp = (struct wpabuf *)eap_sm_build_identity_resp(sm, ehdr->identifier, 0); goto send_resp; } else if (type == EAP_TYPE_NOTIFICATION) { /*Ignore*/ goto out; } else if (type == EAP_TYPE_EXPANDED) { if (plen < sizeof(*ehdr) + 8) { return ESP_FAIL; } reqVendor = WPA_GET_BE24(pos); pos += 3; reqVendorMethod = WPA_GET_BE32(pos); } else { reqVendor = EAP_VENDOR_IETF; reqVendorMethod = type; } if (sm->m && sm->m->process && sm->eap_method_priv && reqVendor == sm->m->vendor && reqVendorMethod == sm->m->method) { resp = sm->m->process(sm, sm->eap_method_priv, &m_res, reqData); } else { m = eap_peer_get_eap_method(reqVendor, reqVendorMethod); if (m == NULL) { goto build_nak; } if (!eap_sm_allowMethod(sm, reqVendor, reqVendorMethod)) { wpa_printf(MSG_DEBUG, "EAP: vendor %" PRIu32 " method %" PRIu32 " not allowed", reqVendor, reqVendorMethod); wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD "vendor=%" PRIu32 " method=%" PRIu32 " -> NAK", reqVendor, reqVendorMethod); goto build_nak; } if (sm->m) { eap_deinit_prev_method(sm, "GET_METHOD"); } sm->m = m; sm->eap_method_priv = sm->m->init(sm); if (sm->eap_method_priv == NULL) { wpa_printf(MSG_ERROR, "Method private structure allocated failure"); sm->m = NULL; goto build_nak; } if (sm->m->process) { resp = sm->m->process(sm, sm->eap_method_priv, &m_res, reqData); } } if (sm->m->isKeyAvailable && sm->m->getKey && sm->m->isKeyAvailable(sm, sm->eap_method_priv)) { if (sm->eapKeyData) { os_free(sm->eapKeyData); } sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, &sm->eapKeyDataLen); } goto send_resp; build_nak: resp = (struct wpabuf *)eap_sm_build_nak(sm, type, ehdr->identifier); if (resp == NULL) { return ESP_FAIL; } send_resp: if (resp == NULL) { wpa_printf(MSG_ERROR, "Response build fail, return."); wpabuf_free(sm->lastRespData); sm->lastRespData = resp; wpa2_set_eap_state(WPA2_ENT_EAP_STATE_FAIL); return WPA2_ENT_EAP_STATE_FAIL; } ret = eap_sm_send_eapol(sm, resp); if (resp != sm->lastRespData) { wpabuf_free(sm->lastRespData); } if (ret != ESP_OK) { wpabuf_free(resp); resp = NULL; if (ret == WPA_ERR_INVALID_BSSID) { ret = WPA2_ENT_EAP_STATE_FAIL; wpa2_set_eap_state(WPA2_ENT_EAP_STATE_FAIL); } } sm->lastRespData = resp; out: return ret; } static int eap_sm_rx_eapol(u8 *src_addr, u8 *buf, u32 len, uint8_t *bssid) { struct eap_sm *sm = gEapSm; if (!sm) { return ESP_FAIL; } #ifdef USE_WPA2_TASK { struct wpa2_rx_param *param = (struct wpa2_rx_param *)os_zalloc(sizeof(struct wpa2_rx_param)); /* free in task */ if (!param) { return ESP_ERR_NO_MEM; } param->buf = (u8 *)os_zalloc(len); /* free in task */ if (!param->buf) { os_free(param); return ESP_ERR_NO_MEM; } param->bssid = bssid; memcpy(param->buf, buf, len); param->len = len; memcpy(param->sa, src_addr, WPA_ADDR_LEN); wpa2_rxq_enqueue(param); return wpa2_post(SIG_WPA2_RX, 0); } #else return eap_sm_rx_eapol_internal(src_addr, buf, len, bssid); #endif } static int wpa2_ent_rx_eapol(u8 *src_addr, u8 *buf, u32 len, uint8_t *bssid) { struct ieee802_1x_hdr *hdr; int ret = ESP_OK; hdr = (struct ieee802_1x_hdr *) buf; switch (hdr->type) { case IEEE802_1X_TYPE_EAPOL_START: case IEEE802_1X_TYPE_EAP_PACKET: case IEEE802_1X_TYPE_EAPOL_LOGOFF: ret = eap_sm_rx_eapol(src_addr, buf, len, bssid); break; case IEEE802_1X_TYPE_EAPOL_KEY: ret = wpa_sm_rx_eapol(src_addr, buf, len); break; default: wpa_printf(MSG_ERROR, "Unknown EAPOL packet type - %d", hdr->type); break; } return ret; } static int eap_sm_rx_eapol_internal(u8 *src_addr, u8 *buf, u32 len, uint8_t *bssid) { struct eap_sm *sm = gEapSm; u32 plen, data_len; struct ieee802_1x_hdr *hdr; struct eap_hdr *ehdr; struct wpabuf *req = NULL; u8 *tmp; int ret = ESP_FAIL; if (!sm) { return ESP_FAIL; } if (len < sizeof(*hdr) + sizeof(*ehdr)) { wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short to be a WPA " "EAPOL-Key (len %lu, expecting at least %lu)", (unsigned long) len, (unsigned long) sizeof(*hdr) + sizeof(*ehdr)); return ESP_FAIL; } tmp = buf; hdr = (struct ieee802_1x_hdr *) tmp; ehdr = (struct eap_hdr *) (hdr + 1); plen = be_to_host16(hdr->length); data_len = plen + sizeof(*hdr); wpa_printf(MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%" PRId32 "", hdr->version, hdr->type, plen); if (hdr->version < EAPOL_VERSION) { /* TODO: backwards compatibility */ } if (hdr->type != IEEE802_1X_TYPE_EAP_PACKET) { wpa_printf(MSG_DEBUG, "EAP: EAP frame (type %u) discarded, " "not a EAP PACKET frame", hdr->type); ret = -2; goto _out; } if (plen > len - sizeof(*hdr) || plen < sizeof(*ehdr)) { wpa_printf(MSG_DEBUG, "EAP: EAPOL frame payload size %lu " "invalid (frame size %lu)", (unsigned long) plen, (unsigned long) len); ret = -2; goto _out; } wpa_hexdump(MSG_MSGDUMP, "EAP: RX EAPOL-EAP PACKET", tmp, len); if (data_len < len) { wpa_printf(MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE " "802.1X data\n", (unsigned long) len - data_len); } #ifdef EAP_PEER_METHOD switch (ehdr->code) { case EAP_CODE_REQUEST: /* Handle EAP-reauthentication case */ if (sm->finish_state == WPA2_ENT_EAP_STATE_SUCCESS) { wpa_printf(MSG_INFO, "EAP Re-authentication in progress"); wpa2_set_eap_state(WPA2_ENT_EAP_STATE_IN_PROGRESS); } req = wpabuf_alloc_copy((u8 *)ehdr, len - sizeof(*hdr)); ret = eap_sm_process_request(sm, req); break; case EAP_CODE_RESPONSE: /*Ignore*/ break; case EAP_CODE_SUCCESS: if (sm->eapKeyData) { wpa_set_pmk(sm->eapKeyData, 0, NULL, false); os_free(sm->eapKeyData); sm->eapKeyData = NULL; wpa_printf(MSG_INFO, ">>>>>EAP FINISH"); ret = WPA2_ENT_EAP_STATE_SUCCESS; wpa2_set_eap_state(WPA2_ENT_EAP_STATE_SUCCESS); eap_deinit_prev_method(sm, "EAP Success"); } else { wpa_printf(MSG_INFO, ">>>>>EAP FAILED, receive EAP_SUCCESS but pmk is empty, potential attack!"); ret = WPA2_ENT_EAP_STATE_FAIL; wpa2_set_eap_state(WPA2_ENT_EAP_STATE_FAIL); } break; case EAP_CODE_FAILURE: wpa_printf(MSG_INFO, ">>>>>EAP FAILED"); ret = WPA2_ENT_EAP_STATE_FAIL; wpa2_set_eap_state(WPA2_ENT_EAP_STATE_FAIL); break; } _out: wpabuf_free(req); #endif return ret; } static int wpa2_start_eapol(void) { #ifdef USE_WPA2_TASK return wpa2_post(SIG_WPA2_START, 0); #else return wpa2_start_eapol_internal(); #endif } static int wpa2_start_eapol_internal(void) { struct eap_sm *sm = gEapSm; int ret = 0; u8 bssid[6]; u8 *buf; size_t len; if (!sm) { return ESP_FAIL; } if (wpa_sta_cur_pmksa_matches_akm()) { wpa_printf(MSG_DEBUG, "RSN: PMKSA caching - do not send EAPOL-Start"); return ESP_FAIL; } ret = esp_wifi_get_assoc_bssid_internal(bssid); if (ret != 0) { wpa_printf(MSG_ERROR, "bssid is empty!"); return WPA_ERR_INVALID_BSSID; } buf = wpa_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START, (u8 *)"", 0, &len, NULL); if (!buf) { return ESP_FAIL; } wpa2_set_eap_state(WPA2_ENT_EAP_STATE_IN_PROGRESS); wpa_ether_send(sm, bssid, ETH_P_EAPOL, buf, len); wpa_free_eapol(buf); return ESP_OK; } /** * eap_peer_sm_init - Allocate and initialize EAP peer state machine * @eapol_ctx: Context data to be used with eapol_cb calls * @eapol_cb: Pointer to EAPOL callback functions * @msg_ctx: Context data for wpa_msg() calls * @conf: EAP configuration * Returns: Pointer to the allocated EAP state machine or %NULL on failure * * This function allocates and initializes an EAP state machine. In addition, * this initializes TLS library for the new EAP state machine. eapol_cb pointer * will be in use until eap_peer_sm_deinit() is used to deinitialize this EAP * state machine. Consequently, the caller must make sure that this data * structure remains alive while the EAP state machine is active. */ static int eap_peer_sm_init(void) { int ret = 0; struct eap_sm *sm; if (gEapSm) { wpa_printf(MSG_ERROR, "EAP: EAP sm not null, deinit it"); eap_peer_sm_deinit(); } sm = (struct eap_sm *)os_zalloc(sizeof(*sm)); if (sm == NULL) { ret = ESP_ERR_NO_MEM; return ret; } gEapSm = sm; s_wpa2_data_lock = os_recursive_mutex_create(); if (!s_wpa2_data_lock) { wpa_printf(MSG_ERROR, "EAP eap_peer_sm_init: failed to alloc data lock"); ret = ESP_ERR_NO_MEM; goto _err; } wpa2_set_eap_state(WPA2_ENT_EAP_STATE_NOT_START); sm->current_identifier = 0xff; esp_wifi_get_macaddr_internal(WIFI_IF_STA, sm->ownaddr); ret = eap_peer_blob_init(sm); if (ret) { wpa_printf(MSG_ERROR, "eap_peer_blob_init failed"); ret = ESP_FAIL; goto _err; } ret = eap_peer_config_init(sm, g_wpa_private_key_passwd, g_wpa_private_key_passwd_len); if (ret) { wpa_printf(MSG_ERROR, "eap_peer_config_init failed"); ret = ESP_FAIL; goto _err; } sm->ssl_ctx = tls_init(NULL); if (sm->ssl_ctx == NULL) { wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS context."); ret = ESP_FAIL; goto _err; } wpa2_rxq_init(); gEapSm = sm; #ifdef USE_WPA2_TASK s_wpa2_queue = os_queue_create(SIG_WPA2_MAX, sizeof(s_wpa2_queue)); ret = os_task_create(wpa2_task, "wpa2T", WPA2_TASK_STACK_SIZE, NULL, WPA2_TASK_PRIORITY, &s_wpa2_task_hdl); if (ret != TRUE) { wpa_printf(MSG_ERROR, "wps enable: failed to create task"); ret = ESP_FAIL; goto _err; } s_wifi_wpa2_sync_sem = os_semphr_create(1, 0); if (!s_wifi_wpa2_sync_sem) { wpa_printf(MSG_ERROR, "EAP: failed create wifi EAP task sync sem"); ret = ESP_FAIL; goto _err; } wpa_printf(MSG_INFO, "wifi_task prio:%d, stack:%d", WPA2_TASK_PRIORITY, WPA2_TASK_STACK_SIZE); #endif return ESP_OK; _err: eap_peer_sm_deinit(); return ret; } /** * eap_peer_sm_deinit - Deinitialize and free an EAP peer state machine * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * * This function deinitializes EAP state machine and frees all allocated * resources. */ static void eap_peer_sm_deinit(void) { struct eap_sm *sm = gEapSm; if (sm == NULL) { return; } eap_peer_config_deinit(sm); eap_peer_blob_deinit(sm); eap_deinit_prev_method(sm, "EAP deinit"); eap_sm_abort(sm); tls_deinit(sm->ssl_ctx); #ifdef USE_WPA2_TASK wpa2_task_delete(0); #endif if (STAILQ_FIRST((&s_wpa2_rxq)) != NULL) { wpa2_rxq_deinit(); } if (s_wifi_wpa2_sync_sem) { os_semphr_delete(s_wifi_wpa2_sync_sem); s_wifi_wpa2_sync_sem = NULL; } if (s_wpa2_data_lock) { os_semphr_delete(s_wpa2_data_lock); s_wpa2_data_lock = NULL; wpa_printf(MSG_DEBUG, "EAP: eap_peer_sm_deinit: free data lock"); } if (s_wpa2_queue) { os_queue_delete(s_wpa2_queue); s_wpa2_queue = NULL; } os_free(sm); gEapSm = NULL; } static esp_err_t esp_client_enable_fn(void *arg) { struct wpa2_funcs *wpa2_cb; wpa_printf(MSG_INFO, "WiFi Enterprise enable"); wpa2_cb = (struct wpa2_funcs *)os_zalloc(sizeof(struct wpa2_funcs)); if (wpa2_cb == NULL) { wpa_printf(MSG_ERROR, "EAP: no mem for eap cb"); return ESP_ERR_NO_MEM; } wpa2_cb->wpa2_sm_rx_eapol = wpa2_ent_rx_eapol; wpa2_cb->wpa2_start = wpa2_start_eapol; wpa2_cb->wpa2_init = eap_peer_sm_init; wpa2_cb->wpa2_deinit = eap_peer_sm_deinit; esp_wifi_register_wpa2_cb_internal(wpa2_cb); wpa_printf(MSG_DEBUG, "WiFi Enterprise crypto init.\r"); #ifdef EAP_PEER_METHOD if (eap_peer_register_methods()) { wpa_printf(MSG_ERROR, "Register EAP Peer methods Failure"); } #endif return ESP_OK; } esp_err_t esp_wifi_sta_enterprise_enable(void) { wifi_wpa2_param_t param; esp_err_t ret; struct wpa_sm *sm = &gWpaSm; wpa2_api_lock(); if (wpa2_is_enabled()) { wpa_printf(MSG_INFO, "EAP: already enabled"); wpa2_api_unlock(); return ESP_OK; } param.fn = (wifi_wpa2_fn_t)esp_client_enable_fn; param.param = NULL; ret = esp_wifi_sta_wpa2_ent_enable_internal(¶m); if (ESP_OK == ret) { wpa2_set_state(WPA2_STATE_ENABLED); sm->wpa_sm_eap_disable = esp_wifi_sta_enterprise_disable; } else { wpa_printf(MSG_ERROR, "failed to enable eap ret=%d", ret); } wpa2_api_unlock(); return ret; } static esp_err_t eap_client_disable_fn(void *param) { struct wpa_sm *sm = &gWpaSm; wpa_printf(MSG_INFO, "WiFi enterprise disable"); esp_wifi_unregister_wpa2_cb_internal(); if (gEapSm) { eap_peer_sm_deinit(); } #ifdef EAP_PEER_METHOD eap_peer_unregister_methods(); #endif sm->wpa_sm_eap_disable = NULL; return ESP_OK; } esp_err_t esp_wifi_sta_enterprise_disable(void) { wifi_wpa2_param_t param; esp_err_t ret; wpa2_api_lock(); if (wpa2_is_disabled()) { wpa_printf(MSG_INFO, "EAP: already disabled"); wpa2_api_unlock(); return ESP_OK; } param.fn = (wifi_wpa2_fn_t)eap_client_disable_fn; param.param = 0; ret = esp_wifi_sta_wpa2_ent_disable_internal(¶m); if (ESP_OK == ret) { wpa2_set_state(WPA2_STATE_DISABLED); } else { wpa_printf(MSG_ERROR, "failed to disable eap ret=%d", ret); } wpa2_api_unlock(); return ret; } esp_err_t esp_eap_client_set_certificate_and_key(const unsigned char *client_cert, int client_cert_len, const unsigned char *private_key, int private_key_len, const unsigned char *private_key_passwd, int private_key_passwd_len) { if (client_cert && client_cert_len > 0) { g_wpa_client_cert = client_cert; g_wpa_client_cert_len = client_cert_len; } if (private_key && private_key_len > 0) { g_wpa_private_key = private_key; g_wpa_private_key_len = private_key_len; } if (private_key_passwd && private_key_passwd_len > 0) { g_wpa_private_key_passwd = private_key_passwd; g_wpa_private_key_passwd_len = private_key_passwd_len; } return ESP_OK; } void esp_eap_client_clear_certificate_and_key(void) { g_wpa_client_cert = NULL; g_wpa_client_cert_len = 0; g_wpa_private_key = NULL; g_wpa_private_key_len = 0; g_wpa_private_key_passwd = NULL; g_wpa_private_key_passwd_len = 0; os_free(g_wpa_pac_file); g_wpa_pac_file = NULL; g_wpa_pac_file_len = 0; } esp_err_t esp_eap_client_set_ca_cert(const unsigned char *ca_cert, int ca_cert_len) { if (ca_cert && ca_cert_len > 0) { g_wpa_ca_cert = ca_cert; g_wpa_ca_cert_len = ca_cert_len; } return ESP_OK; } void esp_eap_client_clear_ca_cert(void) { g_wpa_ca_cert = NULL; g_wpa_ca_cert_len = 0; } #define ANONYMOUS_ID_LEN_MAX 128 esp_err_t esp_eap_client_set_identity(const unsigned char *identity, int len) { if (len <= 0 || len > ANONYMOUS_ID_LEN_MAX) { return ESP_ERR_INVALID_ARG; } if (g_wpa_anonymous_identity) { os_free(g_wpa_anonymous_identity); g_wpa_anonymous_identity = NULL; } g_wpa_anonymous_identity = (u8 *)os_zalloc(len); if (g_wpa_anonymous_identity == NULL) { return ESP_ERR_NO_MEM; } os_memcpy(g_wpa_anonymous_identity, identity, len); g_wpa_anonymous_identity_len = len; return ESP_OK; } void esp_eap_client_clear_identity(void) { if (g_wpa_anonymous_identity) { os_free(g_wpa_anonymous_identity); } g_wpa_anonymous_identity = NULL; g_wpa_anonymous_identity_len = 0; } #define USERNAME_LEN_MAX 128 esp_err_t esp_eap_client_set_username(const unsigned char *username, int len) { if (len <= 0 || len > USERNAME_LEN_MAX) { return ESP_ERR_INVALID_ARG; } if (g_wpa_username) { os_free(g_wpa_username); g_wpa_username = NULL; } g_wpa_username = (u8 *)os_zalloc(len); if (g_wpa_username == NULL) { return ESP_ERR_NO_MEM; } os_memcpy(g_wpa_username, username, len); g_wpa_username_len = len; return ESP_OK; } void esp_eap_client_clear_username(void) { if (g_wpa_username) { os_free(g_wpa_username); } g_wpa_username = NULL; g_wpa_username_len = 0; } esp_err_t esp_eap_client_set_password(const unsigned char *password, int len) { if (len <= 0) { return ESP_ERR_INVALID_ARG; } if (g_wpa_password) { os_free(g_wpa_password); g_wpa_password = NULL; } g_wpa_password = (u8 *)os_zalloc(len); if (g_wpa_password == NULL) { return ESP_ERR_NO_MEM; } os_memcpy(g_wpa_password, password, len); g_wpa_password_len = len; return ESP_OK; } void esp_eap_client_clear_password(void) { if (g_wpa_password) { os_free(g_wpa_password); } g_wpa_password = NULL; g_wpa_password_len = 0; } esp_err_t esp_eap_client_set_new_password(const unsigned char *new_password, int len) { if (len <= 0) { return ESP_ERR_INVALID_ARG; } if (g_wpa_new_password) { os_free(g_wpa_new_password); g_wpa_new_password = NULL; } g_wpa_new_password = (u8 *)os_zalloc(len); if (g_wpa_new_password == NULL) { return ESP_ERR_NO_MEM; } os_memcpy(g_wpa_new_password, new_password, len); g_wpa_password_len = len; return ESP_OK; } void esp_eap_client_clear_new_password(void) { if (g_wpa_new_password) { os_free(g_wpa_new_password); } g_wpa_new_password = NULL; g_wpa_new_password_len = 0; } esp_err_t esp_eap_client_set_disable_time_check(bool disable) { s_disable_time_check = disable; return ESP_OK; } bool wifi_sta_get_enterprise_disable_time_check(void) { return s_disable_time_check; } esp_err_t esp_eap_client_get_disable_time_check(bool *disable) { *disable = wifi_sta_get_enterprise_disable_time_check(); return ESP_OK; } esp_err_t esp_eap_client_set_ttls_phase2_method(esp_eap_ttls_phase2_types type) { switch (type) { case ESP_EAP_TTLS_PHASE2_EAP: g_wpa_ttls_phase2_type = "auth=EAP"; break; case ESP_EAP_TTLS_PHASE2_MSCHAPV2: g_wpa_ttls_phase2_type = "auth=MSCHAPV2"; break; case ESP_EAP_TTLS_PHASE2_MSCHAP: g_wpa_ttls_phase2_type = "auth=MSCHAP"; break; case ESP_EAP_TTLS_PHASE2_PAP: g_wpa_ttls_phase2_type = "auth=PAP"; break; case ESP_EAP_TTLS_PHASE2_CHAP: g_wpa_ttls_phase2_type = "auth=CHAP"; break; default: g_wpa_ttls_phase2_type = "auth=MSCHAPV2"; break; } return ESP_OK; } esp_err_t esp_eap_client_set_suiteb_192bit_certification(bool enable) { #ifdef CONFIG_SUITEB192 g_wpa_suiteb_certification = enable; return ESP_OK; #else return ESP_FAIL; #endif } esp_err_t esp_eap_client_set_pac_file(const unsigned char *pac_file, int pac_file_len) { if (pac_file && pac_file_len > -1) { if (pac_file_len < 512) { // The file contains less than 1 pac and is to be rewritten later g_wpa_pac_file = (u8 *)os_zalloc(512); if (g_wpa_pac_file == NULL) { return ESP_ERR_NO_MEM; } g_wpa_pac_file_len = 0; } else { // The file contains pac data g_wpa_pac_file = (u8 *)os_zalloc(pac_file_len); if (g_wpa_pac_file == NULL) { return ESP_ERR_NO_MEM; } os_memcpy(g_wpa_pac_file, pac_file, pac_file_len); g_wpa_pac_file_len = pac_file_len; } } else { return ESP_FAIL; } return ESP_OK; } esp_err_t esp_eap_client_set_fast_params(esp_eap_fast_config config) { char config_for_supplicant[PHASE1_PARAM_STRING_LEN] = ""; if ((config.fast_provisioning > -1) && (config.fast_provisioning <= 2)) { os_snprintf((char *) &config_for_supplicant, PHASE1_PARAM_STRING_LEN, "fast_provisioning=%d ", config.fast_provisioning); } else { return ESP_ERR_INVALID_ARG; } if (config.fast_max_pac_list_len && config.fast_max_pac_list_len < 100) { os_snprintf((char *) &config_for_supplicant + strlen(config_for_supplicant), PHASE1_PARAM_STRING_LEN - strlen(config_for_supplicant), "fast_max_pac_list_len=%d ", config.fast_max_pac_list_len); } else if (config.fast_max_pac_list_len >= 100) { return ESP_ERR_INVALID_ARG; } if (config.fast_pac_format_binary) { os_strcat((char *) &config_for_supplicant, (const char *) "fast_pac_format=binary"); } // Free the old buffer if it already exists if (g_wpa_phase1_options != NULL) { os_free(g_wpa_phase1_options); } g_wpa_phase1_options = (char *)os_zalloc(sizeof(config_for_supplicant)); if (g_wpa_phase1_options == NULL) { return ESP_ERR_NO_MEM; } os_memcpy(g_wpa_phase1_options, &config_for_supplicant, sizeof(config_for_supplicant)); return ESP_OK; } esp_err_t esp_eap_client_use_default_cert_bundle(bool use_default_bundle) { #ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE g_wpa_default_cert_bundle = use_default_bundle; if (use_default_bundle) { esp_crt_bundle_attach_fn = esp_crt_bundle_attach; } else { esp_crt_bundle_attach_fn = NULL; } return ESP_OK; #else return ESP_FAIL; #endif }