diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index df4564b78..a0a11397b 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -62,6 +62,7 @@ extern "C" { /** EAP authentication failed due to no response received */ #define WPA_EVENT_EAP_TIMEOUT_FAILURE "CTRL-EVENT-EAP-TIMEOUT-FAILURE " #define WPA_EVENT_EAP_TIMEOUT_FAILURE2 "CTRL-EVENT-EAP-TIMEOUT-FAILURE2 " +#define WPA_EVENT_EAP_ERROR_CODE "EAP-ERROR-CODE " /** Network block temporarily disabled (e.g., due to authentication failure) */ #define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED " /** Temporarily disabled network block re-enabled */ diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index 004370706..e55e2d523 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -95,6 +95,14 @@ static void eap_notify_status(struct eap_sm *sm, const char *status, } +static void eap_report_error(struct eap_sm *sm, int error_code) +{ + wpa_printf(MSG_DEBUG, "EAP: Error notification: %d", error_code); + if (sm->eapol_cb->notify_eap_error) + sm->eapol_cb->notify_eap_error(sm->eapol_ctx, error_code); +} + + static void eap_sm_free_key(struct eap_sm *sm) { if (sm->eapKeyData) { @@ -2018,6 +2026,15 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) case EAP_CODE_FAILURE: wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure"); eap_notify_status(sm, "completion", "failure"); + + /* Get the error code from method */ + if (sm->m && sm->m->get_error_code) { + int error_code; + + error_code = sm->m->get_error_code(sm->eap_method_priv); + if (error_code != NO_EAP_METHOD_ERROR) + eap_report_error(sm, error_code); + } sm->rxFailure = TRUE; break; case EAP_CODE_INITIATE: diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h index b5591a01f..d0837e37a 100644 --- a/src/eap_peer/eap.h +++ b/src/eap_peer/eap.h @@ -246,6 +246,13 @@ struct eapol_callbacks { void (*notify_status)(void *ctx, const char *status, const char *parameter); + /** + * notify_eap_error - Report EAP method error code + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @error_code: Error code from the used EAP method + */ + void (*notify_eap_error)(void *ctx, int error_code); + #ifdef CONFIG_EAP_PROXY /** * eap_proxy_cb - Callback signifying any updates from eap_proxy diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index 7a6bfc99f..a4441413f 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -56,6 +56,7 @@ struct eap_aka_data { int kdf_negotiation; u16 last_kdf_attrs[EAP_AKA_PRIME_KDF_MAX]; size_t last_kdf_count; + int error_code; }; @@ -99,6 +100,9 @@ static void * eap_aka_init(struct eap_sm *sm) data->eap_method = EAP_TYPE_AKA; + /* Zero is a valid error code, so we need to initialize */ + data->error_code = NO_EAP_METHOD_ERROR; + eap_aka_state(data, CONTINUE); data->prev_id = -1; @@ -1180,6 +1184,7 @@ static struct wpabuf * eap_aka_process_notification( eap_sim_report_notification(sm->msg_ctx, attr->notification, 1); if (attr->notification >= 0 && attr->notification < 32768) { + data->error_code = attr->notification; eap_aka_state(data, FAILURE); } else if (attr->notification == EAP_SIM_SUCCESS && data->state == RESULT_SUCCESS) @@ -1524,6 +1529,23 @@ static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static int eap_aka_get_error_code(void *priv) +{ + struct eap_aka_data *data = priv; + int current_data_error; + + if (!data) + return NO_EAP_METHOD_ERROR; + + current_data_error = data->error_code; + + /* Now reset for next transaction */ + data->error_code = NO_EAP_METHOD_ERROR; + + return current_data_error; +} + + int eap_peer_aka_register(void) { struct eap_method *eap; @@ -1544,6 +1566,7 @@ int eap_peer_aka_register(void) eap->init_for_reauth = eap_aka_init_for_reauth; eap->get_identity = eap_aka_get_identity; eap->get_emsk = eap_aka_get_emsk; + eap->get_error_code = eap_aka_get_error_code; return eap_peer_method_register(eap); } @@ -1571,6 +1594,7 @@ int eap_peer_aka_prime_register(void) eap->init_for_reauth = eap_aka_init_for_reauth; eap->get_identity = eap_aka_get_identity; eap->get_emsk = eap_aka_get_emsk; + eap->get_error_code = eap_aka_get_error_code; return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index 6ab24834d..096f0f28e 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -14,6 +14,8 @@ #include "eap_peer/eap.h" #include "eap_common/eap_common.h" +#define NO_EAP_METHOD_ERROR (-1) + /* RFC 4137 - EAP Peer state machine */ typedef enum { @@ -205,6 +207,17 @@ struct eap_method { */ const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len); + /** + * get_error_code - Get the latest EAP method error code + * @priv: Pointer to private EAP method data from eap_method::init() + * Returns: An int for the EAP method specific error code if exists or + * NO_EAP_METHOD_ERROR otherwise. + * + * This method is an optional handler that only EAP methods that need to + * report their error code need to implement. + */ + int (*get_error_code)(void *priv); + /** * free - Free EAP method data * @method: Pointer to the method data registered with diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c index cd687cbf8..ba5eea9dd 100644 --- a/src/eap_peer/eap_sim.c +++ b/src/eap_peer/eap_sim.c @@ -47,6 +47,7 @@ struct eap_sim_data { } state; int result_ind, use_result_ind; int use_pseudonym; + int error_code; }; @@ -94,6 +95,9 @@ static void * eap_sim_init(struct eap_sm *sm) return NULL; } + /* Zero is a valid error code, so we need to initialize */ + data->error_code = NO_EAP_METHOD_ERROR; + data->min_num_chal = 2; if (config && config->phase1) { char *pos = os_strstr(config->phase1, "sim_min_num_chal="); @@ -920,6 +924,7 @@ static struct wpabuf * eap_sim_process_notification( eap_sim_report_notification(sm->msg_ctx, attr->notification, 0); if (attr->notification >= 0 && attr->notification < 32768) { + data->error_code = attr->notification; eap_sim_state(data, FAILURE); } else if (attr->notification == EAP_SIM_SUCCESS && data->state == RESULT_SUCCESS) @@ -1244,6 +1249,23 @@ static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static int eap_sim_get_error_code(void *priv) +{ + struct eap_sim_data *data = priv; + int current_data_error; + + if (!data) + return NO_EAP_METHOD_ERROR; + + current_data_error = data->error_code; + + /* Now reset for next transaction */ + data->error_code = NO_EAP_METHOD_ERROR; + + return current_data_error; +} + + int eap_peer_sim_register(void) { struct eap_method *eap; @@ -1264,6 +1286,7 @@ int eap_peer_sim_register(void) eap->init_for_reauth = eap_sim_init_for_reauth; eap->get_identity = eap_sim_get_identity; eap->get_emsk = eap_sim_get_emsk; + eap->get_error_code = eap_sim_get_error_code; return eap_peer_method_register(eap); } diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index 8e4f0e46d..bfbc995f6 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -2015,6 +2015,15 @@ static void eapol_sm_notify_status(void *ctx, const char *status, } +static void eapol_sm_notify_eap_error(void *ctx, int error_code) +{ + struct eapol_sm *sm = ctx; + + if (sm->ctx->eap_error_cb) + sm->ctx->eap_error_cb(sm->ctx->ctx, error_code); +} + + #ifdef CONFIG_EAP_PROXY static void eapol_sm_eap_proxy_cb(void *ctx) @@ -2062,6 +2071,7 @@ static const struct eapol_callbacks eapol_cb = eapol_sm_eap_param_needed, eapol_sm_notify_cert, eapol_sm_notify_status, + eapol_sm_notify_eap_error, #ifdef CONFIG_EAP_PROXY eapol_sm_eap_proxy_cb, eapol_sm_eap_proxy_notify_sim_status, diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index a25c79989..74f40bb1c 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -271,6 +271,13 @@ struct eapol_ctx { void (*status_cb)(void *ctx, const char *status, const char *parameter); + /** + * eap_error_cb - Notification of EAP method error + * @ctx: Callback context (ctx) + * @error_code: EAP method error code + */ + void (*eap_error_cb)(void *ctx, int error_code); + #ifdef CONFIG_EAP_PROXY /** * eap_proxy_cb - Callback signifying any updates from eap_proxy diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c index 3e67a77f2..83df04f39 100644 --- a/wpa_supplicant/notify.c +++ b/wpa_supplicant/notify.c @@ -816,6 +816,12 @@ void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status, } +void wpas_notify_eap_error(struct wpa_supplicant *wpa_s, int error_code) +{ + wpa_msg(wpa_s, MSG_ERROR, WPA_EVENT_EAP_ERROR_CODE "%d", error_code); +} + + void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h index 0c113cab5..3ca933c76 100644 --- a/wpa_supplicant/notify.h +++ b/wpa_supplicant/notify.h @@ -134,6 +134,7 @@ void wpas_notify_preq(struct wpa_supplicant *wpa_s, const u8 *ie, size_t ie_len, u32 ssi_signal); void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status, const char *parameter); +void wpas_notify_eap_error(struct wpa_supplicant *wpa_s, int error_code); void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s, diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index e44f6afad..4634ed7fc 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -1038,6 +1038,14 @@ static void wpa_supplicant_status_cb(void *ctx, const char *status, } +static void wpa_supplicant_eap_error_cb(void *ctx, int error_code) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_eap_error(wpa_s, error_code); +} + + static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len) { struct wpa_supplicant *wpa_s = ctx; @@ -1115,6 +1123,7 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s) ctx->cert_cb = wpa_supplicant_cert_cb; ctx->cert_in_cb = wpa_s->conf->cert_in_cb; ctx->status_cb = wpa_supplicant_status_cb; + ctx->eap_error_cb = wpa_supplicant_eap_error_cb; ctx->set_anon_id = wpa_supplicant_set_anon_id; ctx->cb_ctx = wpa_s; wpa_s->eapol = eapol_sm_init(ctx);