From ddf111d2c4625bff39e4f1e026d3753eb0c43091 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Mon, 14 Sep 2020 15:51:16 +0200 Subject: [PATCH] eapol: IP Allocation KDE support Support IP allocation during the 4-Way Handshake as defined in the P2P spec. This is the supplicant side implementation. The API requires the user to set hs->support_ip_allocation true before eapol_start(). On HANDSHAKE_EVENT_COMPLETE, if this same flag is still set, we've received the IP lease, the netmask and the authenticator's IP from the authenticator and there's no need to start DHCP. If the flag is cleared, the user needs to use DHCP. --- src/eapol.c | 76 +++++++++++++++++++++++++++++++++++++++++++------ src/handshake.h | 9 +++++- 2 files changed, 76 insertions(+), 9 deletions(-) diff --git a/src/eapol.c b/src/eapol.c index 977f720b..acf07c41 100644 --- a/src/eapol.c +++ b/src/eapol.c @@ -1088,7 +1088,7 @@ static void eapol_handle_ptk_1_of_4(struct eapol_sm *sm, const uint8_t *kck; struct eapol_key *step2; uint8_t mic[MIC_MAXLEN]; - uint8_t *ies; + uint8_t ies[512]; size_t ies_len; const uint8_t *own_ie = sm->handshake->supplicant_ie; const uint8_t *pmkid; @@ -1199,8 +1199,6 @@ static void eapol_handle_ptk_1_of_4(struct eapol_sm *sm, * Rebuild the RSNE to include the PMKR1Name and append * MDE + FTE. */ - ies = alloca(512); - rsn_info.num_pmkids = 1; rsn_info.pmkids = sm->handshake->pmk_r1_name; @@ -1214,7 +1212,16 @@ static void eapol_handle_ptk_1_of_4(struct eapol_sm *sm, ies_len += fte[1] + 2; } else { ies_len = own_ie[1] + 2; - ies = (uint8_t *) own_ie; + memcpy(ies, own_ie, ies_len); + } + + if (sm->handshake->support_ip_allocation) { + /* Wi-Fi P2P Technical Specification v1.7 Table 58 */ + ies[ies_len++] = IE_TYPE_VENDOR_SPECIFIC; + ies[ies_len++] = 4 + 1; + l_put_be32(HANDSHAKE_KDE_IP_ADDRESS_REQ, ies + ies_len); + ies_len += 4; + ies[ies_len++] = 0x01; } step2 = eapol_create_ptk_2_of_4(sm->protocol_version, @@ -1381,7 +1388,8 @@ static const uint8_t *eapol_find_rsne(const uint8_t *data, size_t data_len, return first; } -static const uint8_t *eapol_find_osen(const uint8_t *data, size_t data_len) +static const uint8_t *eapol_find_wfa_kde(const uint8_t *data, size_t data_len, + uint8_t oi_type) { struct ie_tlv_iter iter; @@ -1389,7 +1397,7 @@ static const uint8_t *eapol_find_osen(const uint8_t *data, size_t data_len) while (ie_tlv_iter_next(&iter)) { if (ie_tlv_iter_get_tag(&iter) == IE_TYPE_VENDOR_SPECIFIC) { - if (!is_ie_wfa_ie(iter.data, iter.len, IE_WFA_OI_OSEN)) + if (!is_ie_wfa_ie(iter.data, iter.len, oi_type)) continue; } else continue; @@ -1474,6 +1482,28 @@ static const uint8_t *eapol_find_wpa_ie(const uint8_t *data, size_t data_len) return NULL; } +static bool eapol_check_ip_mask(const uint8_t *mask, + const uint8_t *ip1, const uint8_t *ip2) +{ + uint32_t mask_uint = l_get_be32(mask); + uint32_t ip1_uint = l_get_be32(ip1); + uint32_t ip2_uint = l_get_be32(ip2); + + return + /* Check IPs are in the same subnet */ + ((ip1_uint ^ ip2_uint) & mask_uint) == 0 && + /* Check IPs are different */ + ip1_uint != ip2_uint && + /* Check IPs are not subnet addresses */ + (ip1_uint & ~mask_uint) != 0 && + (ip2_uint & ~mask_uint) != 0 && + /* Check IPs are not broadcast addresses */ + (ip1_uint | mask_uint) != 0xffffffff && + (ip2_uint | mask_uint) != 0xffffffff && + /* Check the 1s are at the start of the mask */ + (uint32_t) (mask_uint << __builtin_popcountl(mask_uint)) == 0; +} + static void eapol_handle_ptk_3_of_4(struct eapol_sm *sm, const struct eapol_key *ek, const uint8_t *decrypted_key_data, @@ -1520,8 +1550,9 @@ static void eapol_handle_ptk_3_of_4(struct eapol_sm *sm, rsne = eapol_find_wpa_ie(decrypted_key_data, decrypted_key_data_size); else if (sm->handshake->osen_ie) - rsne = eapol_find_osen(decrypted_key_data, - decrypted_key_data_size); + rsne = eapol_find_wfa_kde(decrypted_key_data, + decrypted_key_data_size, + IE_WFA_OI_OSEN); else rsne = eapol_find_rsne(decrypted_key_data, decrypted_key_data_size, @@ -1667,6 +1698,35 @@ static void eapol_handle_ptk_3_of_4(struct eapol_sm *sm, } else igtk = NULL; + if (sm->handshake->support_ip_allocation) { + const uint8_t *ip_alloc_kde = + eapol_find_wfa_kde(decrypted_key_data, + decrypted_key_data_size, + HANDSHAKE_KDE_IP_ADDRESS_ALLOC & 255); + + if (ip_alloc_kde && + (ip_alloc_kde[1] < 16 || + !eapol_check_ip_mask(ip_alloc_kde + 10, + ip_alloc_kde + 6, + ip_alloc_kde + 14))) { + l_debug("Invalid IP Allocation KDE in frame 3/4"); + handshake_failed(sm, MMPDU_REASON_CODE_INVALID_IE); + return; + } + + sm->handshake->support_ip_allocation = ip_alloc_kde != NULL; + + if (ip_alloc_kde) { + sm->handshake->client_ip_addr = + l_get_be32(ip_alloc_kde + 6); + sm->handshake->subnet_mask = + l_get_be32(ip_alloc_kde + 10); + sm->handshake->go_ip_addr = + l_get_be32(ip_alloc_kde + 14); + } else + l_debug("Authenticator ignored our IP Address Request"); + } + retransmit: /* * 802.11-2016, Section 12.7.6.4: diff --git a/src/handshake.h b/src/handshake.h index c88bb0d1..b738efd9 100644 --- a/src/handshake.h +++ b/src/handshake.h @@ -28,8 +28,8 @@ struct handshake_state; enum crypto_cipher; -/* 802.11-2016 Table 12-6 in section 12.7.2 */ enum handshake_kde { + /* 802.11-2016 Table 12-6 in section 12.7.2 */ HANDSHAKE_KDE_GTK = 0x000fac01, HANDSHAKE_KDE_MAC_ADDRESS = 0x000fac03, HANDSHAKE_KDE_PMKID = 0x000fac04, @@ -41,6 +41,9 @@ enum handshake_kde { HANDSHAKE_KDE_KEY_ID = 0x000fac0a, HANDSHAKE_KDE_MULTIBAND_GTK = 0x000fac0b, HANDSHAKE_KDE_MULTIBAND_KEY_ID = 0x000fac0c, + /* Wi-Fi P2P Technical Specification v1.7 4.2.8 */ + HANDSHAKE_KDE_IP_ADDRESS_REQ = 0x506f9a04, + HANDSHAKE_KDE_IP_ADDRESS_ALLOC = 0x506f9a05, }; enum handshake_event { @@ -124,6 +127,10 @@ struct handshake_state { uint8_t proto_version : 2; unsigned int gtk_index; struct erp_cache_entry *erp_cache; + bool support_ip_allocation : 1; + uint32_t client_ip_addr; + uint32_t subnet_mask; + uint32_t go_ip_addr; void *user_data; void (*free)(struct handshake_state *s);