ap: removed code that was moved to eapol
All handshake packet handling has been removed from ap and moved to eapol. After association, the AP registers a new authenticator state machine which handles the AP side of the handshake. AP will receive a handshake event once the 4-way handshake is complete.
This commit is contained in:
parent
1d40c6cee7
commit
4ed30ff661
619
src/ap.c
619
src/ap.c
|
@ -56,7 +56,6 @@ struct ap_state {
|
||||||
uint8_t pmk[32];
|
uint8_t pmk[32];
|
||||||
struct l_queue *frame_watch_ids;
|
struct l_queue *frame_watch_ids;
|
||||||
uint32_t start_stop_cmd_id;
|
uint32_t start_stop_cmd_id;
|
||||||
uint32_t eapol_watch_id;
|
|
||||||
uint32_t netdev_watch_id;
|
uint32_t netdev_watch_id;
|
||||||
|
|
||||||
uint16_t last_aid;
|
uint16_t last_aid;
|
||||||
|
@ -75,14 +74,8 @@ struct sta_state {
|
||||||
struct ap_state *ap;
|
struct ap_state *ap;
|
||||||
uint8_t *assoc_rsne;
|
uint8_t *assoc_rsne;
|
||||||
size_t assoc_rsne_len;
|
size_t assoc_rsne_len;
|
||||||
uint64_t key_replay_counter;
|
struct eapol_sm *sm;
|
||||||
uint8_t anonce[32];
|
struct handshake_state *hs;
|
||||||
uint8_t snonce[32];
|
|
||||||
uint8_t ptk[64];
|
|
||||||
unsigned int frame_retry;
|
|
||||||
struct l_timeout *frame_timeout;
|
|
||||||
bool have_anonce : 1;
|
|
||||||
bool ptk_complete : 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct l_genl_family *nl80211 = NULL;
|
static struct l_genl_family *nl80211 = NULL;
|
||||||
|
@ -99,8 +92,8 @@ static void ap_sta_free(void *data)
|
||||||
if (sta->assoc_resp_cmd_id)
|
if (sta->assoc_resp_cmd_id)
|
||||||
l_genl_family_cancel(nl80211, sta->assoc_resp_cmd_id);
|
l_genl_family_cancel(nl80211, sta->assoc_resp_cmd_id);
|
||||||
|
|
||||||
if (sta->frame_timeout)
|
eapol_sm_free(sta->sm);
|
||||||
l_timeout_remove(sta->frame_timeout);
|
handshake_state_free(sta->hs);
|
||||||
|
|
||||||
l_free(sta);
|
l_free(sta);
|
||||||
}
|
}
|
||||||
|
@ -128,8 +121,6 @@ static void ap_free(void *data)
|
||||||
if (ap->start_stop_cmd_id)
|
if (ap->start_stop_cmd_id)
|
||||||
l_genl_family_cancel(nl80211, ap->start_stop_cmd_id);
|
l_genl_family_cancel(nl80211, ap->start_stop_cmd_id);
|
||||||
|
|
||||||
eapol_frame_watch_remove(ap->eapol_watch_id);
|
|
||||||
|
|
||||||
netdev_watch_remove(netdev, ap->netdev_watch_id);
|
netdev_watch_remove(netdev, ap->netdev_watch_id);
|
||||||
|
|
||||||
l_queue_destroy(ap->sta_states, ap_sta_free);
|
l_queue_destroy(ap->sta_states, ap_sta_free);
|
||||||
|
@ -159,100 +150,30 @@ static void ap_del_sta_cb(struct l_genl_msg *msg, void *user_data)
|
||||||
l_error("DEL_STATION failed: %i", l_genl_msg_get_error(msg));
|
l_error("DEL_STATION failed: %i", l_genl_msg_get_error(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ap_new_key_cb(struct l_genl_msg *msg, void *user_data)
|
|
||||||
{
|
|
||||||
if (l_genl_msg_get_error(msg) < 0)
|
|
||||||
l_error("NEW_KEY failed: %i", l_genl_msg_get_error(msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ap_del_key_cb(struct l_genl_msg *msg, void *user_data)
|
static void ap_del_key_cb(struct l_genl_msg *msg, void *user_data)
|
||||||
{
|
{
|
||||||
if (l_genl_msg_get_error(msg) < 0)
|
if (l_genl_msg_get_error(msg) < 0)
|
||||||
l_debug("DEL_KEY failed: %i", l_genl_msg_get_error(msg));
|
l_debug("DEL_KEY failed: %i", l_genl_msg_get_error(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ap_new_rsna(struct ap_state *ap, struct sta_state *sta)
|
static void ap_new_rsna(struct sta_state *sta)
|
||||||
{
|
{
|
||||||
struct l_genl_msg *msg;
|
l_debug("STA "MAC" authenticated", MAC_STR(sta->addr));
|
||||||
uint32_t ifindex = device_get_ifindex(ap->device);
|
|
||||||
struct nl80211_sta_flag_update flags = {
|
|
||||||
.mask = (1 << NL80211_STA_FLAG_AUTHORIZED) |
|
|
||||||
(1 << NL80211_STA_FLAG_MFP),
|
|
||||||
.set = (1 << NL80211_STA_FLAG_AUTHORIZED),
|
|
||||||
};
|
|
||||||
uint8_t key_id = 0;
|
|
||||||
const struct crypto_ptk *ptk = (struct crypto_ptk *) sta->ptk;
|
|
||||||
uint32_t cipher = ie_rsn_cipher_suite_to_cipher(ap->ciphers);
|
|
||||||
uint32_t key_type = NL80211_KEYTYPE_PAIRWISE;
|
|
||||||
uint8_t tk_buf[32];
|
|
||||||
|
|
||||||
msg = l_genl_msg_new_sized(NL80211_CMD_SET_STATION, 128);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, 6, sta->addr);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_STA_FLAGS2, 8, &flags);
|
|
||||||
|
|
||||||
if (!l_genl_family_send(nl80211, msg, ap_set_sta_cb, NULL, NULL)) {
|
|
||||||
l_genl_msg_unref(msg);
|
|
||||||
l_error("Issuing SET_STATION failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sta->rsna = true;
|
sta->rsna = true;
|
||||||
|
|
||||||
switch (cipher) {
|
|
||||||
case CRYPTO_CIPHER_CCMP:
|
|
||||||
/*
|
/*
|
||||||
* 802.11-2016 12.8.3 Mapping PTK to CCMP keys:
|
* TODO: Once new AP interface is implemented this is where a
|
||||||
* "A STA shall use the temporal key as the CCMP key
|
* new "ConnectedPeer" property will be added.
|
||||||
* for MPDUs between the two communicating STAs."
|
|
||||||
*/
|
*/
|
||||||
memcpy(tk_buf, ptk->tk, 16);
|
|
||||||
break;
|
|
||||||
case CRYPTO_CIPHER_TKIP:
|
|
||||||
/*
|
|
||||||
* 802.11-2016 12.8.1 Mapping PTK to TKIP keys:
|
|
||||||
* "A STA shall use bits 0-127 of the temporal key as its
|
|
||||||
* input to the TKIP Phase 1 and Phase 2 mixing functions.
|
|
||||||
*
|
|
||||||
* A STA shall use bits 128-191 of the temporal key as
|
|
||||||
* the michael key for MSDUs from the Authenticator's STA
|
|
||||||
* to the Supplicant's STA.
|
|
||||||
*
|
|
||||||
* A STA shall use bits 192-255 of the temporal key as
|
|
||||||
* the michael key for MSDUs from the Supplicant's STA
|
|
||||||
* to the Authenticator's STA."
|
|
||||||
*/
|
|
||||||
memcpy(tk_buf + NL80211_TKIP_DATA_OFFSET_ENCR_KEY, ptk->tk, 16);
|
|
||||||
memcpy(tk_buf + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY,
|
|
||||||
ptk->tk + 16, 8);
|
|
||||||
memcpy(tk_buf + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
|
|
||||||
ptk->tk + 24, 8);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
l_error("Unexpected cipher: %x", cipher);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg = l_genl_msg_new_sized(NL80211_CMD_NEW_KEY, 128);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, 6, sta->addr);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_KEY_IDX, 1, &key_id);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_KEY_DATA,
|
|
||||||
crypto_cipher_key_len(cipher), tk_buf);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_KEY_TYPE, 4, &key_type);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_KEY_CIPHER, 4, &cipher);
|
|
||||||
|
|
||||||
if (!l_genl_family_send(nl80211, msg, ap_new_key_cb, NULL, NULL)) {
|
|
||||||
l_genl_msg_unref(msg);
|
|
||||||
l_error("Issuing NEW_KEY failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ap_drop_rsna(struct ap_state *ap, struct sta_state *sta)
|
static void ap_error_deauth_sta(struct sta_state *sta,
|
||||||
|
enum mmpdu_reason_code reason);
|
||||||
|
|
||||||
|
static void ap_drop_rsna(struct sta_state *sta)
|
||||||
{
|
{
|
||||||
struct l_genl_msg *msg;
|
struct l_genl_msg *msg;
|
||||||
uint32_t ifindex = device_get_ifindex(ap->device);
|
uint32_t ifindex = device_get_ifindex(sta->ap->device);
|
||||||
struct nl80211_sta_flag_update flags = {
|
struct nl80211_sta_flag_update flags = {
|
||||||
.mask = (1 << NL80211_STA_FLAG_AUTHORIZED) |
|
.mask = (1 << NL80211_STA_FLAG_AUTHORIZED) |
|
||||||
(1 << NL80211_STA_FLAG_MFP),
|
(1 << NL80211_STA_FLAG_MFP),
|
||||||
|
@ -262,11 +183,6 @@ static void ap_drop_rsna(struct ap_state *ap, struct sta_state *sta)
|
||||||
|
|
||||||
sta->rsna = false;
|
sta->rsna = false;
|
||||||
|
|
||||||
if (sta->frame_timeout) {
|
|
||||||
l_timeout_remove(sta->frame_timeout);
|
|
||||||
sta->frame_timeout = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg = l_genl_msg_new_sized(NL80211_CMD_SET_STATION, 128);
|
msg = l_genl_msg_new_sized(NL80211_CMD_SET_STATION, 128);
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, 6, sta->addr);
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, 6, sta->addr);
|
||||||
|
@ -287,9 +203,11 @@ static void ap_drop_rsna(struct ap_state *ap, struct sta_state *sta)
|
||||||
l_genl_msg_unref(msg);
|
l_genl_msg_unref(msg);
|
||||||
l_error("Issuing DEL_KEY failed");
|
l_error("Issuing DEL_KEY failed");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#define CIPHER_SUITE_GROUP_NOT_ALLOWED 0x000fac07
|
handshake_state_free(sta->hs);
|
||||||
|
sta->hs = NULL;
|
||||||
|
sta->sm = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void ap_set_rsn_info(struct ap_state *ap, struct ie_rsn_info *rsn)
|
static void ap_set_rsn_info(struct ap_state *ap, struct ie_rsn_info *rsn)
|
||||||
{
|
{
|
||||||
|
@ -299,304 +217,6 @@ static void ap_set_rsn_info(struct ap_state *ap, struct ie_rsn_info *rsn)
|
||||||
rsn->group_cipher = IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC;
|
rsn->group_cipher = IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ap_error_deauth_sta(struct sta_state *sta,
|
|
||||||
enum mmpdu_reason_code reason);
|
|
||||||
|
|
||||||
/* Default dot11RSNAConfigPairwiseUpdateCount value */
|
|
||||||
#define AP_PAIRWISE_UPDATE_COUNT 3
|
|
||||||
|
|
||||||
static void ap_set_eapol_key_timeout(struct sta_state *sta,
|
|
||||||
l_timeout_notify_cb_t cb)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* 802.11-2016 12.7.6.6: "The retransmit timeout value shall be
|
|
||||||
* 100 ms for the first timeout, half the listen interval for the
|
|
||||||
* second timeout, and the listen interval for subsequent timeouts.
|
|
||||||
* If there is no listen interval or the listen interval is zero,
|
|
||||||
* then 100 ms shall be used for all timeout values."
|
|
||||||
*/
|
|
||||||
unsigned int timeout_ms = 100;
|
|
||||||
unsigned int beacon_us = sta->ap->beacon_interval * 1024;
|
|
||||||
|
|
||||||
sta->frame_retry++;
|
|
||||||
|
|
||||||
if (sta->frame_retry == 2 && sta->listen_interval != 0)
|
|
||||||
timeout_ms = sta->listen_interval * beacon_us / 2000;
|
|
||||||
else if (sta->frame_retry > 2 && sta->listen_interval != 0)
|
|
||||||
timeout_ms = sta->listen_interval * beacon_us / 1000;
|
|
||||||
|
|
||||||
if (sta->frame_retry > 1)
|
|
||||||
l_timeout_modify_ms(sta->frame_timeout, timeout_ms);
|
|
||||||
else {
|
|
||||||
if (sta->frame_timeout)
|
|
||||||
l_timeout_remove(sta->frame_timeout);
|
|
||||||
|
|
||||||
sta->frame_timeout = l_timeout_create_ms(timeout_ms, cb, sta,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 802.11-2016 Section 12.7.6.2 */
|
|
||||||
static void ap_send_ptk_1_of_4(struct ap_state *ap, struct sta_state *sta)
|
|
||||||
{
|
|
||||||
uint32_t ifindex = device_get_ifindex(ap->device);
|
|
||||||
const uint8_t *aa = device_get_address(ap->device);
|
|
||||||
uint8_t frame_buf[512];
|
|
||||||
struct eapol_key *ek = (struct eapol_key *) frame_buf;
|
|
||||||
enum crypto_cipher cipher = ie_rsn_cipher_suite_to_cipher(ap->ciphers);
|
|
||||||
uint8_t pmkid[16];
|
|
||||||
|
|
||||||
if (!l_getrandom(sta->anonce, 32)) {
|
|
||||||
l_error("l_getrandom failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sta->have_anonce = true;
|
|
||||||
sta->ptk_complete = false;
|
|
||||||
|
|
||||||
sta->key_replay_counter++;
|
|
||||||
|
|
||||||
memset(ek, 0, sizeof(struct eapol_key));
|
|
||||||
ek->header.protocol_version = EAPOL_PROTOCOL_VERSION_2004;
|
|
||||||
ek->header.packet_type = 0x3;
|
|
||||||
ek->descriptor_type = EAPOL_DESCRIPTOR_TYPE_80211;
|
|
||||||
/* Must be HMAC-SHA1-128 + AES when using CCMP with PSK or 8021X */
|
|
||||||
ek->key_descriptor_version = EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_SHA1_AES;
|
|
||||||
ek->key_type = true;
|
|
||||||
ek->key_ack = true;
|
|
||||||
ek->key_length = L_CPU_TO_BE16(crypto_cipher_key_len(cipher));
|
|
||||||
ek->key_replay_counter = L_CPU_TO_BE64(sta->key_replay_counter);
|
|
||||||
memcpy(ek->key_nonce, sta->anonce, sizeof(ek->key_nonce));
|
|
||||||
|
|
||||||
/* Write the PMKID KDE into Key Data field unencrypted */
|
|
||||||
crypto_derive_pmkid(ap->pmk, sta->addr, aa, pmkid, false);
|
|
||||||
eapol_key_data_append(ek, HANDSHAKE_KDE_PMKID, pmkid, 16);
|
|
||||||
|
|
||||||
ek->header.packet_len = L_CPU_TO_BE16(sizeof(struct eapol_key) +
|
|
||||||
L_BE16_TO_CPU(ek->key_data_len) - 4);
|
|
||||||
|
|
||||||
__eapol_tx_packet(ifindex, sta->addr, ETH_P_PAE,
|
|
||||||
(struct eapol_frame *) ek, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ap_ptk_1_of_4_retry(struct l_timeout *timeout, void *user_data)
|
|
||||||
{
|
|
||||||
struct sta_state *sta = user_data;
|
|
||||||
|
|
||||||
if (sta->frame_retry >= AP_PAIRWISE_UPDATE_COUNT) {
|
|
||||||
ap_error_deauth_sta(sta,
|
|
||||||
MMPDU_REASON_CODE_4WAY_HANDSHAKE_TIMEOUT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ap_send_ptk_1_of_4(sta->ap, sta);
|
|
||||||
|
|
||||||
ap_set_eapol_key_timeout(sta, ap_ptk_1_of_4_retry);
|
|
||||||
|
|
||||||
l_debug("attempt %i", sta->frame_retry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 802.11-2016 Section 12.7.6.4 */
|
|
||||||
static void ap_send_ptk_3_of_4(struct ap_state *ap, struct sta_state *sta)
|
|
||||||
{
|
|
||||||
uint32_t ifindex = device_get_ifindex(ap->device);
|
|
||||||
uint8_t frame_buf[512];
|
|
||||||
uint8_t key_data_buf[128];
|
|
||||||
struct eapol_key *ek = (struct eapol_key *) frame_buf;
|
|
||||||
size_t key_data_len;
|
|
||||||
enum crypto_cipher cipher = ie_rsn_cipher_suite_to_cipher(ap->ciphers);
|
|
||||||
const struct crypto_ptk *ptk = (struct crypto_ptk *) sta->ptk;
|
|
||||||
struct ie_rsn_info rsn;
|
|
||||||
|
|
||||||
sta->key_replay_counter++;
|
|
||||||
|
|
||||||
memset(ek, 0, sizeof(struct eapol_key));
|
|
||||||
ek->header.protocol_version = EAPOL_PROTOCOL_VERSION_2004;
|
|
||||||
ek->header.packet_type = 0x3;
|
|
||||||
ek->descriptor_type = EAPOL_DESCRIPTOR_TYPE_80211;
|
|
||||||
/* Must be HMAC-SHA1-128 + AES when using CCMP with PSK or 8021X */
|
|
||||||
ek->key_descriptor_version = EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_SHA1_AES;
|
|
||||||
ek->key_type = true;
|
|
||||||
ek->install = true;
|
|
||||||
ek->key_ack = true;
|
|
||||||
ek->key_mic = true;
|
|
||||||
ek->secure = true;
|
|
||||||
ek->encrypted_key_data = true;
|
|
||||||
ek->key_length = L_CPU_TO_BE16(crypto_cipher_key_len(cipher));
|
|
||||||
ek->key_replay_counter = L_CPU_TO_BE64(sta->key_replay_counter);
|
|
||||||
memcpy(ek->key_nonce, sta->anonce, sizeof(ek->key_nonce));
|
|
||||||
/*
|
|
||||||
* We don't currently handle group traffic, to support that we'd need
|
|
||||||
* to provide the NL80211_ATTR_KEY_SEQ value from NL80211_CMD_GET_KEY
|
|
||||||
* here.
|
|
||||||
*/
|
|
||||||
l_put_be64(1, ek->key_rsc);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Just one RSNE in Key Data as we only set one cipher in ap->ciphers
|
|
||||||
* currently.
|
|
||||||
*/
|
|
||||||
ap_set_rsn_info(ap, &rsn);
|
|
||||||
if (!ie_build_rsne(&rsn, key_data_buf))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!eapol_encrypt_key_data(ptk->kek, key_data_buf,
|
|
||||||
2 + key_data_buf[1], ek))
|
|
||||||
return;
|
|
||||||
|
|
||||||
key_data_len = L_BE16_TO_CPU(ek->key_data_len);
|
|
||||||
ek->header.packet_len = L_CPU_TO_BE16(sizeof(struct eapol_key) +
|
|
||||||
key_data_len - 4);
|
|
||||||
|
|
||||||
if (!eapol_calculate_mic(ptk->kck, ek, ek->key_mic_data))
|
|
||||||
return;
|
|
||||||
|
|
||||||
__eapol_tx_packet(ifindex, sta->addr, ETH_P_PAE,
|
|
||||||
(struct eapol_frame *) ek, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ap_ptk_3_of_4_retry(struct l_timeout *timeout, void *user_data)
|
|
||||||
{
|
|
||||||
struct sta_state *sta = user_data;
|
|
||||||
|
|
||||||
if (sta->frame_retry >= AP_PAIRWISE_UPDATE_COUNT) {
|
|
||||||
ap_error_deauth_sta(sta,
|
|
||||||
MMPDU_REASON_CODE_4WAY_HANDSHAKE_TIMEOUT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ap_send_ptk_3_of_4(sta->ap, sta);
|
|
||||||
|
|
||||||
ap_set_eapol_key_timeout(sta, ap_ptk_3_of_4_retry);
|
|
||||||
|
|
||||||
l_debug("attempt %i", sta->frame_retry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 802.11-2016 Section 12.7.6.3 */
|
|
||||||
static void ap_handle_ptk_2_of_4(struct sta_state *sta,
|
|
||||||
const struct eapol_key *ek)
|
|
||||||
{
|
|
||||||
const uint8_t *rsne;
|
|
||||||
enum crypto_cipher cipher;
|
|
||||||
size_t ptk_size;
|
|
||||||
uint8_t ptk_buf[64];
|
|
||||||
struct crypto_ptk *ptk = (struct crypto_ptk *) ptk_buf;
|
|
||||||
const uint8_t *aa = device_get_address(sta->ap->device);
|
|
||||||
|
|
||||||
l_debug("");
|
|
||||||
|
|
||||||
if (!eapol_verify_ptk_2_of_4(ek))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (L_BE64_TO_CPU(ek->key_replay_counter) != sta->key_replay_counter)
|
|
||||||
return;
|
|
||||||
|
|
||||||
cipher = ie_rsn_cipher_suite_to_cipher(sta->ap->ciphers);
|
|
||||||
ptk_size = sizeof(struct crypto_ptk) + crypto_cipher_key_len(cipher);
|
|
||||||
|
|
||||||
if (!crypto_derive_pairwise_ptk(sta->ap->pmk, sta->addr, aa,
|
|
||||||
sta->anonce, ek->key_nonce,
|
|
||||||
ptk, ptk_size, false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!eapol_verify_mic(ptk->kck, ek))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Bitwise identical RSNE required */
|
|
||||||
rsne = eapol_find_rsne(ek->key_data,
|
|
||||||
L_BE16_TO_CPU(ek->key_data_len), NULL);
|
|
||||||
if (!rsne || rsne[1] != sta->assoc_rsne_len ||
|
|
||||||
memcmp(rsne + 2, sta->assoc_rsne, rsne[1])) {
|
|
||||||
ap_error_deauth_sta(sta, MMPDU_REASON_CODE_IE_DIFFERENT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(sta->ptk, ptk_buf, ptk_size);
|
|
||||||
memcpy(sta->snonce, ek->key_nonce, sizeof(sta->snonce));
|
|
||||||
sta->ptk_complete = true;
|
|
||||||
|
|
||||||
sta->frame_retry = 0;
|
|
||||||
ap_ptk_3_of_4_retry(NULL, sta);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 802.11-2016 Section 12.7.6.5 */
|
|
||||||
static void ap_handle_ptk_4_of_4(struct sta_state *sta,
|
|
||||||
const struct eapol_key *ek)
|
|
||||||
{
|
|
||||||
const struct crypto_ptk *ptk = (struct crypto_ptk *) sta->ptk;
|
|
||||||
|
|
||||||
l_debug("");
|
|
||||||
|
|
||||||
if (!eapol_verify_ptk_4_of_4(ek, false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (L_BE64_TO_CPU(ek->key_replay_counter) != sta->key_replay_counter)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!eapol_verify_mic(ptk->kck, ek))
|
|
||||||
return;
|
|
||||||
|
|
||||||
l_timeout_remove(sta->frame_timeout);
|
|
||||||
sta->frame_timeout = NULL;
|
|
||||||
|
|
||||||
ap_new_rsna(sta->ap, sta);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ap_eapol_key_handle(struct sta_state *sta,
|
|
||||||
const struct eapol_frame *frame)
|
|
||||||
{
|
|
||||||
size_t frame_len = 4 + L_BE16_TO_CPU(frame->header.packet_len);
|
|
||||||
const struct eapol_key *ek = eapol_key_validate((const void *) frame,
|
|
||||||
frame_len);
|
|
||||||
|
|
||||||
if (!ek)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (ek->request)
|
|
||||||
return; /* Not supported */
|
|
||||||
|
|
||||||
if (!sta->have_anonce)
|
|
||||||
return; /* Not expecting an EAPoL-Key yet */
|
|
||||||
|
|
||||||
if (!sta->ptk_complete)
|
|
||||||
ap_handle_ptk_2_of_4(sta, ek);
|
|
||||||
else if (!sta->rsna)
|
|
||||||
ap_handle_ptk_4_of_4(sta, ek);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ap_eapol_rx(uint16_t proto, const uint8_t *from,
|
|
||||||
const struct eapol_frame *frame, void *user_data)
|
|
||||||
{
|
|
||||||
struct ap_state *ap = user_data;
|
|
||||||
struct sta_state *sta;
|
|
||||||
|
|
||||||
l_debug("");
|
|
||||||
|
|
||||||
if (proto != ETH_P_PAE) {
|
|
||||||
l_error("AP data frame of unknown protocol %04x from %s",
|
|
||||||
proto, util_address_to_string(from));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sta = l_queue_find(ap->sta_states, ap_sta_match_addr, from);
|
|
||||||
if (!sta || !sta->associated) {
|
|
||||||
l_error("AP EAPoL from disassociated STA %s",
|
|
||||||
util_address_to_string(from));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (frame->header.packet_type) {
|
|
||||||
case 3: /* EAPoL-Key */
|
|
||||||
ap_eapol_key_handle(sta, frame);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
l_error("AP received unknown packet type %i from %s",
|
|
||||||
frame->header.packet_type,
|
|
||||||
util_address_to_string(from));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Build a Beacon frame or a Probe Response frame's header and body until
|
* Build a Beacon frame or a Probe Response frame's header and body until
|
||||||
* the TIM IE. Except for the optional TIM IE which is inserted by the
|
* the TIM IE. Except for the optional TIM IE which is inserted by the
|
||||||
|
@ -712,73 +332,20 @@ static uint32_t ap_send_mgmt_frame(struct ap_state *ap,
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ap_associate_sta_cb(struct l_genl_msg *msg, void *user_data)
|
static void ap_handshake_event(struct handshake_state *hs,
|
||||||
|
enum handshake_event event, void *event_data, void *user_data)
|
||||||
{
|
{
|
||||||
struct sta_state *sta = user_data;
|
struct sta_state *sta = user_data;
|
||||||
|
|
||||||
if (l_genl_msg_get_error(msg) < 0) {
|
switch (event) {
|
||||||
l_error("NEW_STATION/SET_STATION failed: %i",
|
case HANDSHAKE_EVENT_COMPLETE:
|
||||||
l_genl_msg_get_error(msg));
|
ap_new_rsna(sta);
|
||||||
return;
|
break;
|
||||||
}
|
case HANDSHAKE_EVENT_FAILED:
|
||||||
|
ap_error_deauth_sta(sta, l_get_u16(event_data));
|
||||||
sta->frame_retry = 0;
|
break;
|
||||||
ap_ptk_1_of_4_retry(NULL, sta);
|
default:
|
||||||
}
|
break;
|
||||||
|
|
||||||
static void ap_associate_sta(struct ap_state *ap, struct sta_state *sta)
|
|
||||||
{
|
|
||||||
struct l_genl_msg *msg;
|
|
||||||
uint32_t ifindex = device_get_ifindex(ap->device);
|
|
||||||
/*
|
|
||||||
* This should hopefully work both with and without
|
|
||||||
* NL80211_FEATURE_FULL_AP_CLIENT_STATE.
|
|
||||||
*/
|
|
||||||
struct nl80211_sta_flag_update flags = {
|
|
||||||
.mask = (1 << NL80211_STA_FLAG_AUTHENTICATED) |
|
|
||||||
(1 << NL80211_STA_FLAG_ASSOCIATED) |
|
|
||||||
(1 << NL80211_STA_FLAG_AUTHORIZED) |
|
|
||||||
(1 << NL80211_STA_FLAG_MFP),
|
|
||||||
.set = (1 << NL80211_STA_FLAG_AUTHENTICATED) |
|
|
||||||
(1 << NL80211_STA_FLAG_ASSOCIATED),
|
|
||||||
};
|
|
||||||
uint8_t rates[256];
|
|
||||||
uint32_t r, minr, maxr, count = 0;
|
|
||||||
uint16_t capability = l_get_le16(&sta->capability);
|
|
||||||
uint8_t cmd = NL80211_CMD_NEW_STATION;
|
|
||||||
|
|
||||||
if (sta->associated)
|
|
||||||
cmd = NL80211_CMD_SET_STATION;
|
|
||||||
|
|
||||||
sta->associated = true;
|
|
||||||
sta->rsna = false;
|
|
||||||
sta->key_replay_counter = 0;
|
|
||||||
|
|
||||||
minr = l_uintset_find_min(sta->rates);
|
|
||||||
maxr = l_uintset_find_max(sta->rates);
|
|
||||||
|
|
||||||
for (r = minr; r <= maxr; r++)
|
|
||||||
if (l_uintset_contains(sta->rates, r))
|
|
||||||
rates[count++] = r;
|
|
||||||
|
|
||||||
msg = l_genl_msg_new_sized(cmd, 300);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, 6, sta->addr);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_STA_AID, 2, &sta->aid);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_STA_FLAGS2, 8, &flags);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_STA_SUPPORTED_RATES,
|
|
||||||
count, &rates);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, 2,
|
|
||||||
&sta->listen_interval);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_STA_CAPABILITY, 2,
|
|
||||||
&capability);
|
|
||||||
|
|
||||||
if (!l_genl_family_send(nl80211, msg, ap_associate_sta_cb, sta, NULL)) {
|
|
||||||
l_genl_msg_unref(msg);
|
|
||||||
if (cmd == NL80211_CMD_NEW_STATION)
|
|
||||||
l_error("Issuing NEW_STATION failed");
|
|
||||||
else
|
|
||||||
l_error("Issuing SET_STATION failed");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -790,11 +357,6 @@ static void ap_disassociate_sta(struct ap_state *ap, struct sta_state *sta)
|
||||||
sta->associated = false;
|
sta->associated = false;
|
||||||
sta->rsna = false;
|
sta->rsna = false;
|
||||||
|
|
||||||
if (sta->frame_timeout) {
|
|
||||||
l_timeout_remove(sta->frame_timeout);
|
|
||||||
sta->frame_timeout = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg = l_genl_msg_new_sized(NL80211_CMD_DEL_STATION, 64);
|
msg = l_genl_msg_new_sized(NL80211_CMD_DEL_STATION, 64);
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, 6, sta->addr);
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, 6, sta->addr);
|
||||||
|
@ -836,6 +398,124 @@ static void ap_error_deauth_sta(struct sta_state *sta,
|
||||||
ap_sta_free(sta);
|
ap_sta_free(sta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ap_associate_sta_cb(struct l_genl_msg *msg, void *user_data)
|
||||||
|
{
|
||||||
|
struct sta_state *sta = user_data;
|
||||||
|
struct ap_state *ap = sta->ap;
|
||||||
|
struct netdev *netdev = device_get_netdev(sta->ap->device);
|
||||||
|
const uint8_t *own_addr = netdev_get_address(netdev);
|
||||||
|
struct scan_bss bss;
|
||||||
|
struct ie_rsn_info rsn;
|
||||||
|
uint8_t bss_rsne[24];
|
||||||
|
|
||||||
|
if (l_genl_msg_get_error(msg) < 0) {
|
||||||
|
l_error("NEW_STATION/SET_STATION failed: %i",
|
||||||
|
l_genl_msg_get_error(msg));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&bss, 0, sizeof(bss));
|
||||||
|
|
||||||
|
ap_set_rsn_info(ap, &rsn);
|
||||||
|
/*
|
||||||
|
* TODO: This assumes the length that ap_set_rsn_info() requires. If
|
||||||
|
* ap_set_rsn_info() changes then this will need to be updated.
|
||||||
|
* Alternatively, sta->assoc_rsne could be used instead, but not
|
||||||
|
* how it sits currently. sta->assoc_rsne only includes the actual RSN
|
||||||
|
* data, not the IE type or length in the header.
|
||||||
|
*/
|
||||||
|
ie_build_rsne(&rsn, bss_rsne);
|
||||||
|
|
||||||
|
if (memcmp(bss_rsne + 2, sta->assoc_rsne, sta->assoc_rsne_len)) {
|
||||||
|
l_error("RSNE from association does not match");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this handshake setup assumes PSK network */
|
||||||
|
sta->hs = netdev_handshake_state_new(netdev);
|
||||||
|
|
||||||
|
handshake_state_set_event_func(sta->hs, ap_handshake_event, sta);
|
||||||
|
handshake_state_set_ssid(sta->hs, (void *)ap->ssid, strlen(ap->ssid));
|
||||||
|
/* ap_rsn/own_rsn can be set equal since the check above matched */
|
||||||
|
handshake_state_set_ap_rsn(sta->hs, bss_rsne);
|
||||||
|
handshake_state_set_own_rsn(sta->hs, bss_rsne);
|
||||||
|
handshake_state_set_pmk(sta->hs, ap->pmk, 32);
|
||||||
|
handshake_state_set_authenticator_address(sta->hs, own_addr);
|
||||||
|
handshake_state_set_supplicant_address(sta->hs, sta->addr);
|
||||||
|
|
||||||
|
sta->sm = eapol_sm_new(sta->hs);
|
||||||
|
if (!sta->sm) {
|
||||||
|
handshake_state_free(sta->hs);
|
||||||
|
l_error("could not create sm object");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
eapol_sm_set_listen_interval(sta->sm, sta->listen_interval);
|
||||||
|
eapol_sm_set_protocol_version(sta->sm, EAPOL_PROTOCOL_VERSION_2004);
|
||||||
|
|
||||||
|
eapol_register_authenticator(sta->sm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
error:
|
||||||
|
ap_disassociate_sta(sta->ap, sta);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ap_associate_sta(struct ap_state *ap, struct sta_state *sta)
|
||||||
|
{
|
||||||
|
struct l_genl_msg *msg;
|
||||||
|
uint32_t ifindex = device_get_ifindex(ap->device);
|
||||||
|
/*
|
||||||
|
* This should hopefully work both with and without
|
||||||
|
* NL80211_FEATURE_FULL_AP_CLIENT_STATE.
|
||||||
|
*/
|
||||||
|
struct nl80211_sta_flag_update flags = {
|
||||||
|
.mask = (1 << NL80211_STA_FLAG_AUTHENTICATED) |
|
||||||
|
(1 << NL80211_STA_FLAG_ASSOCIATED) |
|
||||||
|
(1 << NL80211_STA_FLAG_AUTHORIZED) |
|
||||||
|
(1 << NL80211_STA_FLAG_MFP),
|
||||||
|
.set = (1 << NL80211_STA_FLAG_AUTHENTICATED) |
|
||||||
|
(1 << NL80211_STA_FLAG_ASSOCIATED),
|
||||||
|
};
|
||||||
|
uint8_t rates[256];
|
||||||
|
uint32_t r, minr, maxr, count = 0;
|
||||||
|
uint16_t capability = l_get_le16(&sta->capability);
|
||||||
|
uint8_t cmd = NL80211_CMD_NEW_STATION;
|
||||||
|
|
||||||
|
if (sta->associated)
|
||||||
|
cmd = NL80211_CMD_SET_STATION;
|
||||||
|
|
||||||
|
sta->associated = true;
|
||||||
|
sta->rsna = false;
|
||||||
|
|
||||||
|
minr = l_uintset_find_min(sta->rates);
|
||||||
|
maxr = l_uintset_find_max(sta->rates);
|
||||||
|
|
||||||
|
for (r = minr; r <= maxr; r++)
|
||||||
|
if (l_uintset_contains(sta->rates, r))
|
||||||
|
rates[count++] = r;
|
||||||
|
|
||||||
|
msg = l_genl_msg_new_sized(cmd, 300);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, 6, sta->addr);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_STA_AID, 2, &sta->aid);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_STA_FLAGS2, 8, &flags);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_STA_SUPPORTED_RATES,
|
||||||
|
count, &rates);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, 2,
|
||||||
|
&sta->listen_interval);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_STA_CAPABILITY, 2,
|
||||||
|
&capability);
|
||||||
|
|
||||||
|
if (!l_genl_family_send(nl80211, msg, ap_associate_sta_cb, sta, NULL)) {
|
||||||
|
l_genl_msg_unref(msg);
|
||||||
|
if (cmd == NL80211_CMD_NEW_STATION)
|
||||||
|
l_error("Issuing NEW_STATION failed");
|
||||||
|
else
|
||||||
|
l_error("Issuing SET_STATION failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool ap_common_rates(struct l_uintset *ap_rates,
|
static bool ap_common_rates(struct l_uintset *ap_rates,
|
||||||
struct l_uintset *sta_rates)
|
struct l_uintset *sta_rates)
|
||||||
{
|
{
|
||||||
|
@ -1078,7 +758,7 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
|
||||||
|
|
||||||
/* 802.11-2016 11.3.5.3 j) */
|
/* 802.11-2016 11.3.5.3 j) */
|
||||||
if (sta->rsna)
|
if (sta->rsna)
|
||||||
ap_drop_rsna(ap, sta);
|
ap_drop_rsna(sta);
|
||||||
|
|
||||||
sta->assoc_resp_cmd_id = ap_assoc_resp(ap, sta, sta->addr, sta->aid, 0,
|
sta->assoc_resp_cmd_id = ap_assoc_resp(ap, sta, sta->addr, sta->aid, 0,
|
||||||
reassoc,
|
reassoc,
|
||||||
|
@ -1103,7 +783,7 @@ bad_frame:
|
||||||
* For now, we need to drop the RSNA.
|
* For now, we need to drop the RSNA.
|
||||||
*/
|
*/
|
||||||
if (sta && sta->associated && sta->rsna)
|
if (sta && sta->associated && sta->rsna)
|
||||||
ap_drop_rsna(ap, sta);
|
ap_drop_rsna(sta);
|
||||||
|
|
||||||
if (rates)
|
if (rates)
|
||||||
l_uintset_free(rates);
|
l_uintset_free(rates);
|
||||||
|
@ -1582,7 +1262,6 @@ int ap_start(struct device *device, const char *ssid, const char *psk,
|
||||||
{
|
{
|
||||||
struct netdev *netdev = device_get_netdev(device);
|
struct netdev *netdev = device_get_netdev(device);
|
||||||
struct wiphy *wiphy = device_get_wiphy(device);
|
struct wiphy *wiphy = device_get_wiphy(device);
|
||||||
uint32_t ifindex = device_get_ifindex(device);
|
|
||||||
struct ap_state *ap;
|
struct ap_state *ap;
|
||||||
struct l_genl_msg *cmd;
|
struct l_genl_msg *cmd;
|
||||||
const struct l_queue_entry *entry;
|
const struct l_queue_entry *entry;
|
||||||
|
@ -1659,8 +1338,6 @@ int ap_start(struct device *device, const char *ssid, const char *psk,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ap->eapol_watch_id = eapol_frame_watch_add(ifindex, ap_eapol_rx, ap);
|
|
||||||
|
|
||||||
ap->netdev_watch_id = netdev_watch_add(netdev, ap_netdev_notify, ap);
|
ap->netdev_watch_id = netdev_watch_add(netdev, ap_netdev_notify, ap);
|
||||||
|
|
||||||
if (!ap_list)
|
if (!ap_list)
|
||||||
|
|
Loading…
Reference in New Issue