eapol_sm.c

来自「一个Linux下无线网卡的设置工具」· C语言 代码 · 共 1,729 行 · 第 1/3 页

C
1,729
字号
			SM_ENTER(SUPP_BE, TIMEOUT);		else if (sm->eapSuccess)			SM_ENTER(SUPP_BE, SUCCESS);		break;	}}static void eapol_sm_txLogoff(struct eapol_sm *sm){	wpa_printf(MSG_DEBUG, "EAPOL: txLogoff");	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,			    IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0);	sm->dot1xSuppEapolLogoffFramesTx++;	sm->dot1xSuppEapolFramesTx++;}static void eapol_sm_txStart(struct eapol_sm *sm){	wpa_printf(MSG_DEBUG, "EAPOL: txStart");	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,			    IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0);	sm->dot1xSuppEapolStartFramesTx++;	sm->dot1xSuppEapolFramesTx++;}#define IEEE8021X_ENCR_KEY_LEN 32#define IEEE8021X_SIGN_KEY_LEN 32struct eap_key_data {	u8 encr_key[IEEE8021X_ENCR_KEY_LEN];	u8 sign_key[IEEE8021X_SIGN_KEY_LEN];};static void eapol_sm_processKey(struct eapol_sm *sm){	struct ieee802_1x_hdr *hdr;	struct ieee802_1x_eapol_key *key;	struct eap_key_data keydata;	u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];	u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];	int key_len, res, sign_key_len, encr_key_len;	u16 rx_key_length;	wpa_printf(MSG_DEBUG, "EAPOL: processKey");	if (sm->last_rx_key == NULL)		return;	if (!sm->conf.accept_802_1x_keys) {		wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key"			   " even though this was not accepted - "			   "ignoring this packet");		return;	}	hdr = (struct ieee802_1x_hdr *) sm->last_rx_key;	key = (struct ieee802_1x_eapol_key *) (hdr + 1);	if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) {		wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");		return;	}	rx_key_length = WPA_GET_BE16(key->key_length);	wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d "		   "EAPOL-Key: type=%d key_length=%d key_index=0x%x",		   hdr->version, hdr->type, be_to_host16(hdr->length),		   key->type, rx_key_length, key->key_index);	eapol_sm_notify_lower_layer_success(sm);	sign_key_len = IEEE8021X_SIGN_KEY_LEN;	encr_key_len = IEEE8021X_ENCR_KEY_LEN;	res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata));	if (res < 0) {		wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for "			   "decrypting EAPOL-Key keys");		return;	}	if (res == 16) {		/* LEAP derives only 16 bytes of keying material. */		res = eapol_sm_get_key(sm, (u8 *) &keydata, 16);		if (res) {			wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP "				   "master key for decrypting EAPOL-Key keys");			return;		}		sign_key_len = 16;		encr_key_len = 16;		memcpy(keydata.sign_key, keydata.encr_key, 16);	} else if (res) {		wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key "			   "data for decrypting EAPOL-Key keys (res=%d)", res);		return;	}	/* The key replay_counter must increase when same master key */	if (sm->replay_counter_valid &&	    memcmp(sm->last_replay_counter, key->replay_counter,		   IEEE8021X_REPLAY_COUNTER_LEN) >= 0) {		wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did "			   "not increase - ignoring key");		wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter",			    sm->last_replay_counter,			    IEEE8021X_REPLAY_COUNTER_LEN);		wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter",			    key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN);		return;	}	/* Verify key signature (HMAC-MD5) */	memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN);	memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN);	hmac_md5(keydata.sign_key, sign_key_len,		 sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length),		 key->key_signature);	if (memcmp(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN)	    != 0) {		wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in "			   "EAPOL-Key packet");		memcpy(key->key_signature, orig_key_sign,		       IEEE8021X_KEY_SIGN_LEN);		return;	}	wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");	key_len = be_to_host16(hdr->length) - sizeof(*key);	if (key_len > 32 || rx_key_length > 32) {		wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",			   key_len ? key_len : rx_key_length);		return;	}	if (key_len == rx_key_length) {		memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);		memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,		       encr_key_len);		memcpy(datakey, key + 1, key_len);		rc4(datakey, key_len, ekey,		    IEEE8021X_KEY_IV_LEN + encr_key_len);		wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",				datakey, key_len);	} else if (key_len == 0) {		/*		 * IEEE 802.1X-2004 specifies that least significant Key Length		 * octets from MS-MPPE-Send-Key are used as the key if the key		 * data is not present. This seems to be meaning the beginning		 * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in		 * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator.		 * Anyway, taking the beginning of the keying material from EAP		 * seems to interoperate with Authenticators.		 */		key_len = rx_key_length;		memcpy(datakey, keydata.encr_key, key_len);		wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying "				"material data encryption key",				datakey, key_len);	} else {		wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d "			   "(key_length=%d)", key_len, rx_key_length);		return;	}	sm->replay_counter_valid = TRUE;	memcpy(sm->last_replay_counter, key->replay_counter,	       IEEE8021X_REPLAY_COUNTER_LEN);	wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d "		   "len %d",		   key->key_index & IEEE8021X_KEY_INDEX_FLAG ?		   "unicast" : "broadcast",		   key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len);	if (sm->ctx->set_wep_key &&	    sm->ctx->set_wep_key(sm->ctx->ctx,				 key->key_index & IEEE8021X_KEY_INDEX_FLAG,				 key->key_index & IEEE8021X_KEY_INDEX_MASK,				 datakey, key_len) < 0) {		wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the "			   " driver.");	} else {		if (key->key_index & IEEE8021X_KEY_INDEX_FLAG)			sm->unicast_key_received = TRUE;		else			sm->broadcast_key_received = TRUE;		if ((sm->unicast_key_received ||		     !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) &&		    (sm->broadcast_key_received ||		     !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST)))		{			wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key "				   "frames received");			sm->portValid = TRUE;			if (sm->ctx->eapol_done_cb)				sm->ctx->eapol_done_cb(sm->ctx->ctx);		}	}}static void eapol_sm_getSuppRsp(struct eapol_sm *sm){	wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp");	/* EAP layer processing; no special code is needed, since Supplicant	 * Backend state machine is waiting for eapNoResp or eapResp to be set	 * and these are only set in the EAP state machine when the processing	 * has finished. */}static void eapol_sm_txSuppRsp(struct eapol_sm *sm){	u8 *resp;	size_t resp_len;	wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp");	resp = eap_get_eapRespData(sm->eap, &resp_len);	if (resp == NULL) {		wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data "			   "not available");		return;	}	/* Send EAP-Packet from the EAP layer to the Authenticator */	sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,			    IEEE802_1X_TYPE_EAP_PACKET, resp, resp_len);	/* eapRespData is not used anymore, so free it here */	free(resp);	if (sm->initial_req)		sm->dot1xSuppEapolReqIdFramesRx++;	else		sm->dot1xSuppEapolReqFramesRx++;	sm->dot1xSuppEapolRespFramesTx++;	sm->dot1xSuppEapolFramesTx++;}static void eapol_sm_abortSupp(struct eapol_sm *sm){	/* release system resources that may have been allocated for the	 * authentication session */	free(sm->last_rx_key);	sm->last_rx_key = NULL;	free(sm->eapReqData);	sm->eapReqData = NULL;	eap_sm_abort(sm->eap);}static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx){	eapol_sm_step(timeout_ctx);}/** * eapol_sm_step - EAPOL state machine step function * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * * This function is called to notify the state machine about changed external * variables. It will step through the EAPOL state machines in loop to process * all triggered state changes. */void eapol_sm_step(struct eapol_sm *sm){	int i;	/* In theory, it should be ok to run this in loop until !changed.	 * However, it is better to use a limit on number of iterations to	 * allow events (e.g., SIGTERM) to stop the program cleanly if the	 * state machine were to generate a busy loop. */	for (i = 0; i < 100; i++) {		sm->changed = FALSE;		SM_STEP_RUN(SUPP_PAE);		SM_STEP_RUN(KEY_RX);		SM_STEP_RUN(SUPP_BE);		if (eap_sm_step(sm->eap))			sm->changed = TRUE;	}	if (sm->changed) {		/* restart EAPOL state machine step from timeout call in order		 * to allow other events to be processed. */		eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);		eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm);	}	if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {		int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0;		sm->cb_status = EAPOL_CB_IN_PROGRESS;		sm->ctx->cb(sm, success, sm->ctx->cb_ctx);	}}static const char *eapol_supp_pae_state(int state){	switch (state) {	case SUPP_PAE_LOGOFF:		return "LOGOFF";	case SUPP_PAE_DISCONNECTED:		return "DISCONNECTED";	case SUPP_PAE_CONNECTING:		return "CONNECTING";	case SUPP_PAE_AUTHENTICATING:		return "AUTHENTICATING";	case SUPP_PAE_HELD:		return "HELD";	case SUPP_PAE_AUTHENTICATED:		return "AUTHENTICATED";	case SUPP_PAE_RESTART:		return "RESTART";	default:		return "UNKNOWN";	}}static const char *eapol_supp_be_state(int state){	switch (state) {	case SUPP_BE_REQUEST:		return "REQUEST";	case SUPP_BE_RESPONSE:		return "RESPONSE";	case SUPP_BE_SUCCESS:		return "SUCCESS";	case SUPP_BE_FAIL:		return "FAIL";	case SUPP_BE_TIMEOUT:		return "TIMEOUT";	case SUPP_BE_IDLE:		return "IDLE";	case SUPP_BE_INITIALIZE:		return "INITIALIZE";	case SUPP_BE_RECEIVE:		return "RECEIVE";	default:		return "UNKNOWN";	}}static const char * eapol_port_status(PortStatus status){	if (status == Authorized)		return "Authorized";	else		return "Unauthorized";}static const char * eapol_port_control(PortControl ctrl){	switch (ctrl) {	case Auto:		return "Auto";	case ForceUnauthorized:		return "ForceUnauthorized";	case ForceAuthorized:		return "ForceAuthorized";	default:		return "Unknown";	}}/** * eapol_sm_configure - Set EAPOL variables * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @heldPeriod: dot1xSuppHeldPeriod * @authPeriod: dot1xSuppAuthPeriod * @startPeriod: dot1xSuppStartPeriod * @maxStart: dot1xSuppMaxStart * * Set configurable EAPOL state machine variables. Each variable can be set to * the given value or ignored if set to -1 (to set only some of the variables). */void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,			int startPeriod, int maxStart){	if (sm == NULL)		return;	if (heldPeriod >= 0)		sm->heldPeriod = heldPeriod;	if (authPeriod >= 0)		sm->authPeriod = authPeriod;	if (startPeriod >= 0)		sm->startPeriod = startPeriod;	if (maxStart >= 0)		sm->maxStart = maxStart;}/** * eapol_sm_get_status - Get EAPOL state machine status * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @buf: Buffer for status information * @buflen: Maximum buffer length * @verbose: Whether to include verbose status information * Returns: Number of bytes written to buf. * * Query EAPOL state machine for status information. This function fills in a * text area with current status information from the EAPOL state machine. If * the buffer (buf) is not large enough, status information will be truncated * to fit the buffer. */int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,			int verbose){	int len;	if (sm == NULL)		return 0;	len = snprintf(buf, buflen,		       "Supplicant PAE state=%s\n"		       "suppPortStatus=%s\n",		       eapol_supp_pae_state(sm->SUPP_PAE_state),		       eapol_port_status(sm->suppPortStatus));	if (verbose) {		len += snprintf(buf + len, buflen - len,				"heldPeriod=%u\n"				"authPeriod=%u\n"				"startPeriod=%u\n"				"maxStart=%u\n"				"portControl=%s\n"				"Supplicant Backend state=%s\n",				sm->heldPeriod,				sm->authPeriod,				sm->startPeriod,				sm->maxStart,				eapol_port_control(sm->portControl),				eapol_supp_be_state(sm->SUPP_BE_state));	}	len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose);	return len;}/** * eapol_sm_get_mib - Get EAPOL state machine MIBs * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() * @buf: Buffer for MIB information * @buflen: Maximum buffer length * Returns: Number of bytes written to buf. * * Query EAPOL state machine for MIB information. This function fills in a * text area with current MIB information from the EAPOL state machine. If * the buffer (buf) is not large enough, MIB information will be truncated to * fit the buffer. */int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen){	int len;	if (sm == NULL)		return 0;	len = snprintf(buf, buflen,		       "dot1xSuppPaeState=%d\n"		       "dot1xSuppHeldPeriod=%u\n"		       "dot1xSuppAuthPeriod=%u\n"		       "dot1xSuppStartPeriod=%u\n"		       "dot1xSuppMaxStart=%u\n"		       "dot1xSuppSuppControlledPortStatus=%s\n"		       "dot1xSuppBackendPaeState=%d\n"		       "dot1xSuppEapolFramesRx=%u\n"		       "dot1xSuppEapolFramesTx=%u\n"		       "dot1xSuppEapolStartFramesTx=%u\n"		       "dot1xSuppEapolLogoffFramesTx=%u\n"		       "dot1xSuppEapolRespFramesTx=%u\n"		       "dot1xSuppEapolReqIdFramesRx=%u\n"		       "dot1xSuppEapolReqFramesRx=%u\n"		       "dot1xSuppInvalidEapolFramesRx=%u\n"		       "dot1xSuppEapLengthErrorFramesRx=%u\n"		       "dot1xSuppLastEapolFrameVersion=%u\n"		       "dot1xSuppLastEapolFrameSource=" MACSTR "\n",		       sm->SUPP_PAE_state,		       sm->heldPeriod,		       sm->authPeriod,		       sm->startPeriod,		       sm->maxStart,		       sm->suppPortStatus == Authorized ?		       "Authorized" : "Unauthorized",		       sm->SUPP_BE_state,		       sm->dot1xSuppEapolFramesRx,		       sm->dot1xSuppEapolFramesTx,		       sm->dot1xSuppEapolStartFramesTx,		       sm->dot1xSuppEapolLogoffFramesTx,		       sm->dot1xSuppEapolRespFramesTx,		       sm->dot1xSuppEapolReqIdFramesRx,		       sm->dot1xSuppEapolReqFramesRx,		       sm->dot1xSuppInvalidEapolFramesRx,		       sm->dot1xSuppEapLengthErrorFramesRx,		       sm->dot1xSuppLastEapolFrameVersion,		       MAC2STR(sm->dot1xSuppLastEapolFrameSource));	return len;}/** * 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 plen, data_len;	int res = 1;	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;	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);		}		free(sm->eapReqData);		sm->eapReqDataLen = plen;		sm->eapReqData = malloc(sm->eapReqDataLen);		if (sm->eapReqData) {			wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet "				   "frame");			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) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?