mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-20 12:39:25 +01:00
eapol: WPA-specific handshake quirks
To support WPA allow the legacy EAPOL-Key frame formats.
This commit is contained in:
parent
a8c0f20510
commit
a7846aee85
212
src/eapol.c
212
src/eapol.c
@ -305,7 +305,7 @@ bool eapol_verify_ptk_2_of_4(const struct eapol_key *ek)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool eapol_verify_ptk_3_of_4(const struct eapol_key *ek)
|
||||
bool eapol_verify_ptk_3_of_4(const struct eapol_key *ek, bool is_wpa)
|
||||
{
|
||||
uint16_t key_len;
|
||||
|
||||
@ -318,10 +318,11 @@ bool eapol_verify_ptk_3_of_4(const struct eapol_key *ek)
|
||||
if (!ek->key_mic)
|
||||
return false;
|
||||
|
||||
if (!ek->secure)
|
||||
if (ek->secure != !is_wpa)
|
||||
return false;
|
||||
|
||||
if (!ek->encrypted_key_data)
|
||||
/* Must be encrypted when GTK is present but reserved in WPA */
|
||||
if (!ek->encrypted_key_data && !is_wpa)
|
||||
return false;
|
||||
|
||||
key_len = L_BE16_TO_CPU(ek->key_length);
|
||||
@ -338,7 +339,7 @@ bool eapol_verify_ptk_3_of_4(const struct eapol_key *ek)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool eapol_verify_ptk_4_of_4(const struct eapol_key *ek)
|
||||
bool eapol_verify_ptk_4_of_4(const struct eapol_key *ek, bool is_wpa)
|
||||
{
|
||||
uint16_t key_len;
|
||||
|
||||
@ -351,10 +352,10 @@ bool eapol_verify_ptk_4_of_4(const struct eapol_key *ek)
|
||||
if (!ek->key_mic)
|
||||
return false;
|
||||
|
||||
if (!ek->secure)
|
||||
if (ek->secure != !is_wpa)
|
||||
return false;
|
||||
|
||||
if (ek->encrypted_key_data)
|
||||
if (ek->encrypted_key_data && !is_wpa)
|
||||
return false;
|
||||
|
||||
key_len = L_BE16_TO_CPU(ek->key_length);
|
||||
@ -421,7 +422,8 @@ static struct eapol_key *eapol_create_common(
|
||||
const uint8_t snonce[],
|
||||
size_t extra_len,
|
||||
const uint8_t *extra_data,
|
||||
int key_type)
|
||||
int key_type,
|
||||
bool is_wpa)
|
||||
{
|
||||
size_t to_alloc = sizeof(struct eapol_key);
|
||||
struct eapol_key *out_frame = l_malloc(to_alloc + extra_len);
|
||||
@ -431,7 +433,8 @@ static struct eapol_key *eapol_create_common(
|
||||
out_frame->protocol_version = protocol;
|
||||
out_frame->packet_type = 0x3;
|
||||
out_frame->packet_len = L_CPU_TO_BE16(to_alloc + extra_len - 4);
|
||||
out_frame->descriptor_type = EAPOL_DESCRIPTOR_TYPE_80211;
|
||||
out_frame->descriptor_type = is_wpa ? EAPOL_DESCRIPTOR_TYPE_WPA :
|
||||
EAPOL_DESCRIPTOR_TYPE_80211;
|
||||
out_frame->key_descriptor_version = version;
|
||||
out_frame->key_type = key_type;
|
||||
out_frame->install = false;
|
||||
@ -457,35 +460,41 @@ struct eapol_key *eapol_create_ptk_2_of_4(
|
||||
uint64_t key_replay_counter,
|
||||
const uint8_t snonce[],
|
||||
size_t extra_len,
|
||||
const uint8_t *extra_data)
|
||||
const uint8_t *extra_data,
|
||||
bool is_wpa)
|
||||
{
|
||||
return eapol_create_common(protocol, version, false, key_replay_counter,
|
||||
snonce, extra_len, extra_data, 1);
|
||||
snonce, extra_len, extra_data, 1,
|
||||
is_wpa);
|
||||
}
|
||||
|
||||
struct eapol_key *eapol_create_ptk_4_of_4(
|
||||
enum eapol_protocol_version protocol,
|
||||
enum eapol_key_descriptor_version version,
|
||||
uint64_t key_replay_counter)
|
||||
uint64_t key_replay_counter,
|
||||
bool is_wpa)
|
||||
{
|
||||
uint8_t snonce[32];
|
||||
|
||||
memset(snonce, 0, sizeof(snonce));
|
||||
return eapol_create_common(protocol, version, true, key_replay_counter,
|
||||
snonce, 0, NULL, 1);
|
||||
return eapol_create_common(protocol, version,
|
||||
is_wpa ? false : true,
|
||||
key_replay_counter, snonce, 0, NULL,
|
||||
1, is_wpa);
|
||||
}
|
||||
|
||||
static struct eapol_key *eapol_create_gtk_2_of_2(
|
||||
struct eapol_key *eapol_create_gtk_2_of_2(
|
||||
enum eapol_protocol_version protocol,
|
||||
enum eapol_key_descriptor_version version,
|
||||
uint64_t key_replay_counter)
|
||||
uint64_t key_replay_counter,
|
||||
bool is_wpa)
|
||||
{
|
||||
uint8_t snonce[32];
|
||||
|
||||
memset(snonce, 0, sizeof(snonce));
|
||||
return eapol_create_common(protocol, version, true,
|
||||
key_replay_counter, snonce, 0, NULL,
|
||||
0);
|
||||
0, is_wpa);
|
||||
}
|
||||
|
||||
struct eapol_sm {
|
||||
@ -503,6 +512,7 @@ struct eapol_sm {
|
||||
struct l_timeout *timeout;
|
||||
bool have_snonce:1;
|
||||
bool have_replay:1;
|
||||
bool ptk_complete:1;
|
||||
bool wpa_ie:1;
|
||||
};
|
||||
|
||||
@ -674,7 +684,8 @@ static void eapol_handle_ptk_1_of_4(uint32_t ifindex, struct eapol_sm *sm,
|
||||
ek->key_descriptor_version,
|
||||
sm->replay_counter,
|
||||
sm->snonce,
|
||||
sm->own_ie[1] + 2, sm->own_ie);
|
||||
sm->own_ie[1] + 2, sm->own_ie,
|
||||
sm->wpa_ie);
|
||||
|
||||
if (!eapol_calculate_mic(ptk->kck, step2, mic)) {
|
||||
l_info("MIC calculation failed. "
|
||||
@ -741,13 +752,31 @@ static const uint8_t *eapol_find_rsne(const uint8_t *data, size_t data_len)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const uint8_t *eapol_find_wpa_ie(const uint8_t *data, size_t data_len)
|
||||
{
|
||||
struct ie_tlv_iter iter;
|
||||
|
||||
ie_tlv_iter_init(&iter, data, data_len);
|
||||
|
||||
while (ie_tlv_iter_next(&iter)) {
|
||||
if (ie_tlv_iter_get_tag(&iter) != IE_TYPE_VENDOR_SPECIFIC)
|
||||
continue;
|
||||
|
||||
if (is_ie_wpa_ie(ie_tlv_iter_get_data(&iter),
|
||||
ie_tlv_iter_get_length(&iter)))
|
||||
return ie_tlv_iter_get_data(&iter) - 2;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function performs a match of the RSN IE obtained from the scan
|
||||
* results vs the RSN IE obtained as part of the 4-way handshake. If they
|
||||
* This function performs a match of the RSN/WPA IE obtained from the scan
|
||||
* results vs the RSN/WPA IE obtained as part of the 4-way handshake. If they
|
||||
* don't match, the EAPoL packet must be silently discarded.
|
||||
*/
|
||||
static bool eapol_ap_rsne_matches(const uint8_t *eapol_rsne,
|
||||
const uint8_t *scan_rsne)
|
||||
static bool eapol_ap_ie_matches(const uint8_t *eapol_ie,
|
||||
const uint8_t *scan_ie, bool is_wpa)
|
||||
{
|
||||
struct ie_rsn_info eapol_info;
|
||||
struct ie_rsn_info scan_info;
|
||||
@ -756,21 +785,31 @@ static bool eapol_ap_rsne_matches(const uint8_t *eapol_rsne,
|
||||
* First check that the sizes match, if they do, run a bitwise
|
||||
* comparison.
|
||||
*/
|
||||
if (eapol_rsne[1] == scan_rsne[1] &&
|
||||
!memcmp(eapol_rsne + 2, scan_rsne + 2, eapol_rsne[1]))
|
||||
if (eapol_ie[1] == scan_ie[1] &&
|
||||
!memcmp(eapol_ie + 2, scan_ie + 2, eapol_ie[1]))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Otherwise we have to parse the RSN IEs and compare the individual
|
||||
* Otherwise we have to parse the IEs and compare the individual
|
||||
* fields
|
||||
*/
|
||||
if (ie_parse_rsne_from_data(eapol_rsne, eapol_rsne[1] + 2,
|
||||
&eapol_info) < 0)
|
||||
return false;
|
||||
if (!is_wpa) {
|
||||
if (ie_parse_rsne_from_data(eapol_ie, eapol_ie[1] + 2,
|
||||
&eapol_info) < 0)
|
||||
return false;
|
||||
|
||||
if (ie_parse_rsne_from_data(scan_rsne, scan_rsne[1] + 2,
|
||||
&scan_info) < 0)
|
||||
return false;
|
||||
if (ie_parse_rsne_from_data(scan_ie, scan_ie[1] + 2,
|
||||
&scan_info) < 0)
|
||||
return false;
|
||||
} else {
|
||||
if (ie_parse_wpa_from_data(eapol_ie, eapol_ie[1] + 2,
|
||||
&eapol_info) < 0)
|
||||
return false;
|
||||
|
||||
if (ie_parse_wpa_from_data(scan_ie, scan_ie[1] + 2,
|
||||
&scan_info) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eapol_info.group_cipher != scan_info.group_cipher)
|
||||
return false;
|
||||
@ -838,7 +877,7 @@ static void eapol_handle_ptk_3_of_4(uint32_t ifindex,
|
||||
const uint8_t *rsne;
|
||||
uint8_t gtk_key_index;
|
||||
|
||||
if (!eapol_verify_ptk_3_of_4(ek)) {
|
||||
if (!eapol_verify_ptk_3_of_4(ek, sm->wpa_ie)) {
|
||||
handshake_failed(ifindex, sm, MPDU_REASON_CODE_UNSPECIFIED);
|
||||
return;
|
||||
}
|
||||
@ -857,13 +896,18 @@ static void eapol_handle_ptk_3_of_4(uint32_t ifindex,
|
||||
* not identical to that the STA received in the Beacon or Probe
|
||||
* Response frame, the STA shall disassociate.
|
||||
*/
|
||||
rsne = eapol_find_rsne(decrypted_key_data, decrypted_key_data_size);
|
||||
if (!sm->wpa_ie)
|
||||
rsne = eapol_find_rsne(decrypted_key_data,
|
||||
decrypted_key_data_size);
|
||||
else
|
||||
rsne = eapol_find_wpa_ie(decrypted_key_data,
|
||||
decrypted_key_data_size);
|
||||
if (!rsne) {
|
||||
handshake_failed(ifindex, sm, MPDU_REASON_CODE_IE_DIFFERENT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!eapol_ap_rsne_matches(rsne, sm->ap_ie)) {
|
||||
if (!eapol_ap_ie_matches(rsne, sm->ap_ie, sm->wpa_ie)) {
|
||||
handshake_failed(ifindex, sm, MPDU_REASON_CODE_IE_DIFFERENT);
|
||||
return;
|
||||
}
|
||||
@ -878,22 +922,27 @@ static void eapol_handle_ptk_3_of_4(uint32_t ifindex,
|
||||
/*
|
||||
* TODO: If group_cipher was negotiated, find the GTK and install it
|
||||
*/
|
||||
gtk = eapol_find_gtk_kde(decrypted_key_data, decrypted_key_data_size,
|
||||
>k_len);
|
||||
if (!gtk || gtk_len < 2) {
|
||||
handshake_failed(ifindex, sm, MPDU_REASON_CODE_UNSPECIFIED);
|
||||
return;
|
||||
}
|
||||
if (!sm->wpa_ie) {
|
||||
gtk = eapol_find_gtk_kde(decrypted_key_data,
|
||||
decrypted_key_data_size,
|
||||
>k_len);
|
||||
if (!gtk || gtk_len < 8) {
|
||||
handshake_failed(ifindex, sm,
|
||||
MPDU_REASON_CODE_UNSPECIFIED);
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_key_index = util_bit_field(gtk[0], 0, 2);
|
||||
/* TODO: Handle tx bit */
|
||||
/* TODO: Handle tx bit */
|
||||
|
||||
gtk += 2;
|
||||
gtk_len -= 2;
|
||||
gtk_key_index = util_bit_field(gtk[0], 0, 2);
|
||||
gtk += 2;
|
||||
gtk_len -= 2;
|
||||
} else
|
||||
gtk = NULL;
|
||||
|
||||
step4 = eapol_create_ptk_4_of_4(protocol_version,
|
||||
ek->key_descriptor_version,
|
||||
sm->replay_counter);
|
||||
sm->replay_counter, sm->wpa_ie);
|
||||
|
||||
/*
|
||||
* 802.11-2012, Section 11.6.6.4, step b):
|
||||
@ -907,10 +956,12 @@ static void eapol_handle_ptk_3_of_4(uint32_t ifindex,
|
||||
memcpy(step4->key_mic_data, mic, sizeof(mic));
|
||||
tx_packet(ifindex, sm->aa, sm->spa, step4, user_data);
|
||||
|
||||
sm->ptk_complete = true;
|
||||
|
||||
if (install_tk)
|
||||
install_tk(sm->ifindex, sm->aa, ptk->tk, rsne, sm->user_data);
|
||||
|
||||
if (install_gtk)
|
||||
if (gtk && install_gtk)
|
||||
install_gtk(sm->ifindex, gtk_key_index, gtk, gtk_len,
|
||||
ek->key_rsc, 6, rsne, sm->user_data);
|
||||
|
||||
@ -937,20 +988,31 @@ static void eapol_handle_gtk_1_of_2(uint32_t ifindex,
|
||||
return;
|
||||
}
|
||||
|
||||
gtk = eapol_find_gtk_kde(decrypted_key_data,
|
||||
decrypted_key_data_size,
|
||||
>k_len);
|
||||
if (!sm->wpa_ie) {
|
||||
gtk = eapol_find_gtk_kde(decrypted_key_data,
|
||||
decrypted_key_data_size,
|
||||
>k_len);
|
||||
|
||||
if (!gtk || gtk_len < 8)
|
||||
return;
|
||||
if (!gtk || gtk_len < 8)
|
||||
return;
|
||||
} else {
|
||||
gtk = decrypted_key_data;
|
||||
gtk_len = decrypted_key_data_size;
|
||||
|
||||
gtk_key_index = util_bit_field(gtk[0], 0, 2);
|
||||
gtk += 2;
|
||||
gtk_len -= 2;
|
||||
if (!gtk || gtk_len < 6)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sm->wpa_ie) {
|
||||
gtk_key_index = util_bit_field(gtk[0], 0, 2);
|
||||
gtk += 2;
|
||||
gtk_len -= 2;
|
||||
} else
|
||||
gtk_key_index = ek->wpa_key_id;
|
||||
|
||||
step2 = eapol_create_gtk_2_of_2(protocol_version,
|
||||
ek->key_descriptor_version,
|
||||
sm->replay_counter);
|
||||
sm->replay_counter, sm->wpa_ie);
|
||||
|
||||
/*
|
||||
* 802.11-2012, Section 11.6.7.3, step b):
|
||||
@ -1006,7 +1068,7 @@ void __eapol_rx_packet(uint32_t ifindex, const uint8_t *spa, const uint8_t *aa,
|
||||
struct eapol_sm *sm;
|
||||
struct crypto_ptk *ptk;
|
||||
uint8_t *decrypted_key_data = NULL;
|
||||
size_t decrypted_key_data_len;
|
||||
size_t key_data_len = 0;
|
||||
uint64_t replay_counter;
|
||||
|
||||
ek = eapol_key_validate(frame, len);
|
||||
@ -1021,6 +1083,12 @@ void __eapol_rx_packet(uint32_t ifindex, const uint8_t *spa, const uint8_t *aa,
|
||||
if (!ek->key_ack)
|
||||
return;
|
||||
|
||||
/* Further Descriptor Type check */
|
||||
if (!sm->wpa_ie && ek->descriptor_type != EAPOL_DESCRIPTOR_TYPE_80211)
|
||||
return;
|
||||
else if (sm->wpa_ie && ek->descriptor_type != EAPOL_DESCRIPTOR_TYPE_WPA)
|
||||
return;
|
||||
|
||||
replay_counter = L_BE64_TO_CPU(ek->key_replay_counter);
|
||||
|
||||
/*
|
||||
@ -1049,33 +1117,47 @@ void __eapol_rx_packet(uint32_t ifindex, const uint8_t *spa, const uint8_t *aa,
|
||||
return;
|
||||
}
|
||||
|
||||
if (ek->encrypted_key_data) {
|
||||
if ((ek->encrypted_key_data && !sm->wpa_ie) ||
|
||||
(ek->key_type == 0 && sm->wpa_ie)) {
|
||||
/* Haven't received step 1 yet, so no ptk */
|
||||
if (!sm->have_snonce)
|
||||
return;
|
||||
|
||||
decrypted_key_data = eapol_decrypt_key_data(ptk->kek, ek,
|
||||
&decrypted_key_data_len);
|
||||
&key_data_len);
|
||||
if (!decrypted_key_data)
|
||||
return;
|
||||
}
|
||||
} else
|
||||
key_data_len = L_BE16_TO_CPU(ek->key_data_len);
|
||||
|
||||
/* Check if this is the group key handshake */
|
||||
if (ek->key_type == 0) {
|
||||
if (!ek->key_mic || !ek->secure)
|
||||
/* Only GTK handshake allowed after PTK handshake complete */
|
||||
if (!sm->ptk_complete)
|
||||
goto done;
|
||||
|
||||
eapol_handle_gtk_1_of_2(ifindex, sm, ek, decrypted_key_data,
|
||||
decrypted_key_data_len, user_data);
|
||||
if (!decrypted_key_data)
|
||||
goto done;
|
||||
|
||||
eapol_handle_gtk_1_of_2(ifindex, sm, ek,
|
||||
decrypted_key_data,
|
||||
key_data_len, user_data);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* If no MIC, then assume packet 1, otherwise packet 3 */
|
||||
if (!ek->key_mic)
|
||||
eapol_handle_ptk_1_of_4(ifindex, sm, ek, user_data);
|
||||
else
|
||||
eapol_handle_ptk_3_of_4(ifindex, sm, ek, decrypted_key_data,
|
||||
decrypted_key_data_len, user_data);
|
||||
else {
|
||||
if (sm->ptk_complete)
|
||||
goto done;
|
||||
|
||||
if (!key_data_len)
|
||||
goto done;
|
||||
|
||||
eapol_handle_ptk_3_of_4(ifindex, sm, ek,
|
||||
decrypted_key_data ?: ek->key_data,
|
||||
key_data_len, user_data);
|
||||
}
|
||||
|
||||
done:
|
||||
l_free(decrypted_key_data);
|
||||
|
16
src/eapol.h
16
src/eapol.h
@ -123,8 +123,8 @@ const struct eapol_key *eapol_key_validate(const uint8_t *frame, size_t len);
|
||||
|
||||
bool eapol_verify_ptk_1_of_4(const struct eapol_key *ek);
|
||||
bool eapol_verify_ptk_2_of_4(const struct eapol_key *ek);
|
||||
bool eapol_verify_ptk_3_of_4(const struct eapol_key *ek);
|
||||
bool eapol_verify_ptk_4_of_4(const struct eapol_key *ek);
|
||||
bool eapol_verify_ptk_3_of_4(const struct eapol_key *ek, bool is_wpa);
|
||||
bool eapol_verify_ptk_4_of_4(const struct eapol_key *ek, bool is_wpa);
|
||||
bool eapol_verify_gtk_1_of_2(const struct eapol_key *ek, bool is_wpa);
|
||||
|
||||
struct eapol_key *eapol_create_ptk_2_of_4(
|
||||
@ -133,12 +133,20 @@ struct eapol_key *eapol_create_ptk_2_of_4(
|
||||
uint64_t key_replay_counter,
|
||||
const uint8_t snonce[],
|
||||
size_t extra_len,
|
||||
const uint8_t *extra_data);
|
||||
const uint8_t *extra_data,
|
||||
bool is_wpa);
|
||||
|
||||
struct eapol_key *eapol_create_ptk_4_of_4(
|
||||
enum eapol_protocol_version protocol,
|
||||
enum eapol_key_descriptor_version version,
|
||||
uint64_t key_replay_counter);
|
||||
uint64_t key_replay_counter,
|
||||
bool is_wpa);
|
||||
|
||||
struct eapol_key *eapol_create_gtk_2_of_2(
|
||||
enum eapol_protocol_version protocol,
|
||||
enum eapol_key_descriptor_version version,
|
||||
uint64_t key_replay_counter,
|
||||
bool is_wpa);
|
||||
|
||||
void __eapol_rx_packet(uint32_t ifindex, const uint8_t *spa, const uint8_t *aa,
|
||||
const uint8_t *frame, size_t len,
|
||||
|
Loading…
Reference in New Issue
Block a user