void eapol_sm_initialize(struct eapol_state_machine *sm) { /* Initialize the state machines by asserting initialize and then * deasserting it after one step */ sm->initialize = TRUE; eapol_sm_step(sm); sm->initialize = FALSE; eapol_sm_step(sm); /* Start one second tick for port timers state machine */ eloop_cancel_timeout(eapol_port_timers_tick, sm->rtapd, sm); eloop_register_timeout(1, 0, eapol_port_timers_tick, sm->rtapd, sm); }
/* Port Timers state machine - 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_sm *sm = timeout_ctx; if (sm->authWhile > 0) { sm->authWhile--; if (sm->authWhile == 0) wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0"); } if (sm->heldWhile > 0) { sm->heldWhile--; if (sm->heldWhile == 0) wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0"); } if (sm->startWhen > 0) { sm->startWhen--; if (sm->startWhen == 0) wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0"); } if (sm->idleWhile > 0) { sm->idleWhile--; if (sm->idleWhile == 0) wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0"); } if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) { eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, sm); } else { wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick"); sm->timer_tick_enabled = 0; } eapol_sm_step(sm); }
/** * eapol_sm_notify_logoff - Notification of logon/logoff commands * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @logoff: Whether command was logoff * * Notify EAPOL state machines that user requested logon/logoff. */ void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) { if (sm) { sm->userLogoff = logoff; eapol_sm_step(sm); } }
/** * eapol_sm_notify_portControl - Notification of portControl changes * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @portControl: New value for portControl variable * * Notify EAPOL state machines that portControl variable has changed. */ void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl) { if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAPOL: External notification - " "portControl=%s", eapol_port_control(portControl)); sm->portControl = portControl; eapol_sm_step(sm); }
/** * eapol_sm_notify_portValid - Notification about portValid change * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @valid: New portValid value * * Notify EAPOL state machine about new portValid value. */ void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid) { if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAPOL: External notification - " "portValid=%d", valid); sm->portValid = valid; eapol_sm_step(sm); }
/** * eapol_sm_notify_portEnabled - Notification about portEnabled change * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @enabled: New portEnabled value * * Notify EAPOL state machine about new portEnabled value. */ void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled) { if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAPOL: External notification - " "portEnabled=%d", enabled); sm->portEnabled = enabled; eapol_sm_step(sm); }
/** * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @fail: %TRUE = set failure, %FALSE = clear failure * * Notify EAPOL state machine that external event has forced EAP state to * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE. */ void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail) { if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAPOL: External notification - " "EAP fail=%d", fail); sm->eapFail = fail; sm->altReject = fail; eapol_sm_step(sm); }
/** * eapol_sm_init - Initialize EAPOL state machine * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer * and EAPOL state machine will free it in eapol_sm_deinit() * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure * * Allocate and initialize an EAPOL state machine. */ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) { struct eapol_sm *sm; struct eap_config conf; sm = os_zalloc(sizeof(*sm)); if (sm == NULL) return NULL; sm->ctx = ctx; sm->portControl = Auto; /* Supplicant PAE state machine */ sm->heldPeriod = 60; sm->startPeriod = 30; sm->maxStart = 3; /* Supplicant Backend state machine */ sm->authPeriod = 30; os_memset(&conf, 0, sizeof(conf)); conf.opensc_engine_path = ctx->opensc_engine_path; conf.pkcs11_engine_path = ctx->pkcs11_engine_path; conf.pkcs11_module_path = ctx->pkcs11_module_path; conf.wps = ctx->wps; sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf); if (sm->eap == NULL) { os_free(sm); return NULL; } /* Initialize EAPOL state machines */ sm->initialize = TRUE; eapol_sm_step(sm); sm->initialize = FALSE; eapol_sm_step(sm); sm->timer_tick_enabled = 1; eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); return sm; }
/** * eapol_sm_notify_lower_layer_success - Notification of lower layer success * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @in_eapol_sm: Whether the caller is already running inside EAPOL state * machine loop (eapol_sm_step()) * * Notify EAPOL (and EAP) state machines that a lower layer has detected a * successful authentication. This is used to recover from dropped EAP-Success * messages. */ void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm) { if (sm == NULL) return; WPA_RUN_TEST; WPA_RUN_TEST; eap_notify_lower_layer_success(sm->eap); WPA_RUN_TEST; if (!in_eapol_sm) eapol_sm_step(sm); }
/** * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * * Notify EAPOL state machines that PMKSA caching was successful. This is used * to move EAPOL and EAP state machines into authenticated/successful state. */ void eapol_sm_notify_cached(struct eapol_sm *sm) { if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL"); sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED; sm->suppPortStatus = Authorized; eapol_sm_set_port_authorized(sm); sm->portValid = TRUE; eap_notify_success(sm->eap); eapol_sm_step(sm); }
/** * eapol_sm_notify_eap_success - Notification of external EAP success trigger * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @success: %TRUE = set success, %FALSE = clear success * * Notify the EAPOL state machine that external event has forced EAP state to * success (success = %TRUE). This can be cleared by setting success = %FALSE. * * This function is called to update EAP state when WPA-PSK key handshake has * been completed successfully since WPA-PSK does not use EAP state machine. */ void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success) { if (sm == NULL) return; wpa_printf(MSG_DEBUG, "EAPOL: External notification - " "EAP success=%d", success); sm->eapSuccess = success; sm->altAccept = success; if (success) eap_notify_success(sm->eap); eapol_sm_step(sm); }
/** * eapol_sm_notify_ctrl_response - Notification of received user input * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * * Notify EAPOL state machines that a control response, i.e., user * input, was received in order to trigger retrying of a pending EAP request. */ void eapol_sm_notify_ctrl_response(struct eapol_sm *sm) { if (sm == NULL) return; if (sm->eapReqData && !sm->eapReq) { wpa_printf(MSG_DEBUG, "EAPOL: received control response (user " "input) notification - retrying pending EAP " "Request"); sm->eapolEap = TRUE; sm->eapReq = TRUE; eapol_sm_step(sm); } }
static void eapol_sm_notify_pending(void *ctx) { struct eapol_sm *sm = ctx; if (sm == NULL) return; if (sm->eapReqData && !sm->eapReq) { wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP " "state machine - retrying pending EAP Request"); sm->eapolEap = TRUE; sm->eapReq = TRUE; eapol_sm_step(sm); } }
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->quietWhile > 0) state->quietWhile--; if (state->reAuthWhen > 0) state->reAuthWhen--; if (state->txWhen > 0) state->txWhen--; eapol_sm_step(state); eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state); }
/** * eapol_sm_rx_eapol - Process received EAPOL frames * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @src: Source MAC address of the EAPOL packet * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) * @len: Length of the EAPOL frame * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine, * -1 failure */ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, size_t len) { const struct ieee802_1x_hdr *hdr; const struct ieee802_1x_eapol_key *key; int data_len; int res = 1; size_t plen; if (sm == NULL) return 0; sm->dot1xSuppEapolFramesRx++; if (len < sizeof(*hdr)) { sm->dot1xSuppInvalidEapolFramesRx++; return 0; } hdr = (const struct ieee802_1x_hdr *) buf; sm->dot1xSuppLastEapolFrameVersion = hdr->version; os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN); if (hdr->version < EAPOL_VERSION) { /* TODO: backwards compatibility */ } plen = be_to_host16(hdr->length); if (plen > len - sizeof(*hdr)) { sm->dot1xSuppEapLengthErrorFramesRx++; return 0; } data_len = plen + sizeof(*hdr); switch (hdr->type) { case IEEE802_1X_TYPE_EAP_PACKET: if (sm->cached_pmk) { /* Trying to use PMKSA caching, but Authenticator did * not seem to have a matching entry. Need to restart * EAPOL state machines. */ eapol_sm_abort_cached(sm); } os_free(sm->eapReqData); sm->eapReqDataLen = plen; sm->eapReqData = os_malloc(sm->eapReqDataLen); if (sm->eapReqData) { wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet " "frame"); os_memcpy(sm->eapReqData, (u8 *) (hdr + 1), sm->eapReqDataLen); sm->eapolEap = TRUE; eapol_sm_step(sm); } break; case IEEE802_1X_TYPE_EAPOL_KEY: if (plen < sizeof(*key)) { wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key " "frame received"); break; } key = (const struct ieee802_1x_eapol_key *) (hdr + 1); if (key->type == EAPOL_KEY_TYPE_WPA || key->type == EAPOL_KEY_TYPE_RSN) { /* WPA Supplicant takes care of this frame. */ wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key " "frame in EAPOL state machines"); res = 0; break; } if (key->type != EAPOL_KEY_TYPE_RC4) { wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown " "EAPOL-Key type %d", key->type); break; } os_free(sm->last_rx_key); sm->last_rx_key = os_malloc(data_len); if (sm->last_rx_key) { wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key " "frame"); os_memcpy(sm->last_rx_key, buf, data_len); sm->last_rx_key_len = data_len; sm->rxKey = TRUE; eapol_sm_step(sm); } break; default: wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d", hdr->type); sm->dot1xSuppInvalidEapolFramesRx++; break; } return res; }
static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx) { eapol_sm_step(timeout_ctx); }
/** * eapol_sm_rx_eapol - Process received EAPOL frames * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @src: Source MAC address of the EAPOL packet * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) * @len: Length of the EAPOL frame * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine, * -1 failure */ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, size_t len) { const struct ieee802_1x_hdr *hdr; const struct ieee802_1x_eapol_key *key; int data_len; int res = 1; size_t plen; if (sm == NULL) return 0; sm->dot1xSuppEapolFramesRx++; if (len < sizeof(*hdr)) { sm->dot1xSuppInvalidEapolFramesRx++; return 0; } hdr = (const struct ieee802_1x_hdr *) buf; sm->dot1xSuppLastEapolFrameVersion = hdr->version; os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN); if (hdr->version < EAPOL_VERSION) { /* TODO: backwards compatibility */ } plen = be_to_host16(hdr->length); if (plen > len - sizeof(*hdr)) { sm->dot1xSuppEapLengthErrorFramesRx++; return 0; } #ifdef CONFIG_WPS if (sm->conf.workaround && plen < len - sizeof(*hdr) && hdr->type == IEEE802_1X_TYPE_EAP_PACKET && len - sizeof(*hdr) > sizeof(struct eap_hdr)) { const struct eap_hdr *ehdr = (const struct eap_hdr *) (hdr + 1); u16 elen; elen = be_to_host16(ehdr->length); if (elen > plen && elen <= len - sizeof(*hdr)) { /* * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS * packets with too short EAPOL header length field * (14 octets). This is fixed in firmware Ver.1.49. * As a workaround, fix the EAPOL header based on the * correct length in the EAP packet. */ wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL " "payload length based on EAP header: " "%d -> %d", (int) plen, elen); plen = elen; } } #endif /* CONFIG_WPS */ data_len = plen + sizeof(*hdr); switch (hdr->type) { case IEEE802_1X_TYPE_EAP_PACKET: if (sm->cached_pmk) { /* Trying to use PMKSA caching, but Authenticator did * not seem to have a matching entry. Need to restart * EAPOL state machines. */ eapol_sm_abort_cached(sm); } wpabuf_free(sm->eapReqData); sm->eapReqData = wpabuf_alloc_copy(hdr + 1, plen); if (sm->eapReqData) { wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet " "frame"); sm->eapolEap = TRUE; eapol_sm_step(sm); } break; case IEEE802_1X_TYPE_EAPOL_KEY: if (plen < sizeof(*key)) { wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key " "frame received"); break; } key = (const struct ieee802_1x_eapol_key *) (hdr + 1); if (key->type == EAPOL_KEY_TYPE_WPA || key->type == EAPOL_KEY_TYPE_RSN) { /* WPA Supplicant takes care of this frame. */ wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key " "frame in EAPOL state machines"); res = 0; break; } if (key->type != EAPOL_KEY_TYPE_RC4) { wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown " "EAPOL-Key type %d", key->type); break; } os_free(sm->last_rx_key); sm->last_rx_key = os_malloc(data_len); if (sm->last_rx_key) { wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key " "frame"); os_memcpy(sm->last_rx_key, buf, data_len); sm->last_rx_key_len = data_len; sm->rxKey = TRUE; eapol_sm_step(sm); } break; default: wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d", hdr->type); sm->dot1xSuppInvalidEapolFramesRx++; break; } return res; }