Beispiel #1
0
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);
}
Beispiel #10
0
/**
 * 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);
}
Beispiel #11
0
/**
 * 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);
}
Beispiel #12
0
/**
 * 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);
	}
}
Beispiel #13
0
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);
	}
}
Beispiel #14
0
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;
}
Beispiel #16
0
static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx)
{
	eapol_sm_step(timeout_ctx);
}
Beispiel #17
0
/**
 * 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;
}