ft: netdev: prep for FT isolation into ft.c

Currently netdev handles caching FT auth information and uses FT
parsers/auth-proto to manage the protocol. This sets up to remove
this state machine from netdev and isolate it into ft.c.

This does not break the existing auth-proto (hence the slight
modifications, which will be removed soon).

Eventually the auth-proto will be removed from FT entirely, replaced
just by an FT state machine, similar to how EAPoL works (netdev hooks
to TX/RX frames).
This commit is contained in:
James Prestwood 2022-09-27 12:47:21 -07:00 committed by Denis Kenzior
parent 4c6cc29f4a
commit cd3f82ce8c
3 changed files with 337 additions and 33 deletions

319
src/ft.c
View File

@ -33,6 +33,30 @@
#include "src/mpdu.h"
#include "src/auth-proto.h"
#include "src/band.h"
#include "src/scan.h"
#include "src/util.h"
#include "src/netdev.h"
#include "src/module.h"
static ft_tx_frame_func_t tx_frame = NULL;
static ft_tx_associate_func_t tx_assoc = NULL;
static struct l_queue *info_list = NULL;
struct ft_info {
uint32_t ifindex;
uint8_t spa[6];
uint8_t aa[6];
uint8_t snonce[32];
uint8_t mde[3];
uint8_t *fte;
uint8_t *authenticator_ie;
uint8_t prev_bssid[6];
uint32_t frequency;
struct ie_ft_info ft_info;
bool parsed : 1;
};
struct ft_sm {
struct auth_proto ap;
@ -45,6 +69,8 @@ struct ft_sm {
void *user_data;
bool over_ds : 1;
uint8_t prev_bssid[6];
};
/*
@ -202,11 +228,13 @@ static bool ft_parse_associate_resp_frame(const uint8_t *frame, size_t frame_len
return true;
}
static int ft_tx_reassociate(struct ft_sm *ft)
static int ft_tx_reassociate(uint32_t ifindex, uint32_t freq,
const uint8_t *prev_bssid)
{
struct netdev *netdev = netdev_find(ifindex);
struct handshake_state *hs = netdev_get_handshake(netdev);
struct iovec iov[3];
int iov_elems = 0;
struct handshake_state *hs = ft->hs;
uint32_t kck_len = handshake_state_get_kck_len(hs);
bool is_rsn = hs->supplicant_ie != NULL;
uint8_t *rsne = NULL;
@ -233,9 +261,8 @@ static int ft_tx_reassociate(struct ft_sm *ft)
rsn_info.num_pmkids = 1;
rsn_info.pmkids = hs->pmk_r1_name;
/* Always set OCVC false for FT-over-DS */
if (ft->over_ds)
rsn_info.ocvc = false;
/* Always set OCVC false for FT for now */
rsn_info.ocvc = false;
rsne = alloca(256);
ie_build_rsne(&rsn_info, rsne);
@ -306,7 +333,7 @@ static int ft_tx_reassociate(struct ft_sm *ft)
iov_elems += 1;
}
return ft->tx_assoc(iov, iov_elems, ft->user_data);
return tx_assoc(ifindex, freq, prev_bssid, iov, iov_elems);
error:
return -EINVAL;
@ -348,7 +375,7 @@ static bool ft_verify_rsne(const uint8_t *rsne, const uint8_t *pmk_r0_name,
return true;
}
static int ft_parse_ies(struct handshake_state *hs,
static int parse_ies(struct handshake_state *hs,
const uint8_t *authenticator_ie,
const uint8_t *ies, size_t ies_len,
const uint8_t **mde_out,
@ -480,7 +507,7 @@ bool ft_over_ds_parse_action_ies(struct ft_ds_info *info,
const uint8_t *fte = NULL;
bool is_rsn = hs->supplicant_ie != NULL;
if (ft_parse_ies(hs, info->authenticator_ie, ies, ies_len,
if (parse_ies(hs, info->authenticator_ie, ies, ies_len,
&mde, &fte) < 0)
return false;
@ -512,7 +539,7 @@ static int ft_process_ies(struct handshake_state *hs, const uint8_t *ies,
if (!ies)
goto ft_error;
if (ft_parse_ies(hs, hs->authenticator_ie, ies, ies_len,
if (parse_ies(hs, hs->authenticator_ie, ies, ies_len,
&mde, &fte) < 0)
goto ft_error;
@ -654,11 +681,10 @@ auth_error:
return (int)status_code;
}
static int ft_rx_associate(struct auth_proto *ap, const uint8_t *frame,
size_t frame_len)
int __ft_rx_associate(uint32_t ifindex, const uint8_t *frame, size_t frame_len)
{
struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
struct handshake_state *hs = ft->hs;
struct netdev *netdev = netdev_find(ifindex);
struct handshake_state *hs = netdev_get_handshake(netdev);
uint32_t kck_len = handshake_state_get_kck_len(hs);
const uint8_t *rsne = NULL;
const uint8_t *mde = NULL;
@ -671,6 +697,9 @@ static int ft_rx_associate(struct auth_proto *ap, const uint8_t *frame,
&mde, &fte))
return -EBADMSG;
if (out_status != 0)
return (int)out_status;
/*
* During a transition in an RSN, check for an RSNE containing the
* PMK-R1-Name and the remaining fields same as in the advertised
@ -785,17 +814,25 @@ static int ft_rx_associate(struct auth_proto *ap, const uint8_t *frame,
ft_info.igtk_ipn);
}
handshake_state_install_ptk(ft->hs);
handshake_state_install_ptk(hs);
}
return 0;
}
static int ft_rx_associate(struct auth_proto *ap, const uint8_t *frame,
size_t frame_len)
{
struct ft_sm *sm = l_container_of(ap, struct ft_sm, ap);
return __ft_rx_associate(sm->hs->ifindex, frame, frame_len);
}
static int ft_rx_oci(struct auth_proto *ap)
{
struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
return ft_tx_reassociate(ft);
return ft_tx_reassociate(ft->hs->ifindex, 0, ft->prev_bssid);
}
static void ft_sm_free(struct auth_proto *ap)
@ -809,7 +846,7 @@ static bool ft_over_ds_start(struct auth_proto *ap)
{
struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
return ft_tx_reassociate(ft) == 0;
return ft_tx_reassociate(ft->hs->ifindex, 0, ft->prev_bssid) == 0;
}
bool ft_build_authenticate_ies(struct handshake_state *hs, bool ocvc,
@ -924,6 +961,8 @@ struct auth_proto *ft_over_air_sm_new(struct handshake_state *hs,
ft->ap.free = ft_sm_free;
ft->ap.rx_oci = ft_rx_oci;
memcpy(ft->prev_bssid, hs->aa, 6);
return &ft->ap;
}
@ -942,5 +981,253 @@ struct auth_proto *ft_over_ds_sm_new(struct handshake_state *hs,
ft->ap.start = ft_over_ds_start;
ft->ap.free = ft_sm_free;
memcpy(ft->prev_bssid, hs->aa, 6);
return &ft->ap;
}
void __ft_set_tx_frame_func(ft_tx_frame_func_t func)
{
tx_frame = func;
}
void __ft_set_tx_associate_func(ft_tx_associate_func_t func)
{
tx_assoc = func;
}
static bool ft_parse_ies(struct ft_info *info, struct handshake_state *hs,
const uint8_t *ies, size_t ies_len)
{
const uint8_t *mde = NULL;
const uint8_t *fte = NULL;
bool is_rsn = hs->supplicant_ie != NULL;
if (parse_ies(hs, info->authenticator_ie, ies, ies_len,
&mde, &fte) < 0)
return false;
if (!mde_equal(info->mde, mde))
goto ft_error;
if (is_rsn) {
if (!ft_parse_fte(hs, info->snonce, fte, &info->ft_info))
goto ft_error;
info->fte = l_memdup(fte, fte[1] + 2);
} else if (fte)
goto ft_error;
return true;
ft_error:
return false;
}
static struct ft_info *ft_info_find(uint32_t ifindex, const uint8_t *aa)
{
const struct l_queue_entry *e;
for (e = l_queue_get_entries(info_list); e; e = e->next) {
struct ft_info *info = e->data;
if (info->ifindex != ifindex)
continue;
if (aa && memcmp(info->aa, aa, 6))
continue;
return info;
}
return NULL;
}
void __ft_rx_action(uint32_t ifindex, const uint8_t *frame, size_t frame_len)
{
struct netdev *netdev = netdev_find(ifindex);
struct handshake_state *hs = netdev_get_handshake(netdev);
struct ft_info *info;
int ret;
const uint8_t *aa = NULL;
const uint8_t *spa = NULL;
const uint8_t *ies = NULL;
size_t ies_len = 0;
ret = ft_over_ds_parse_action_response(frame, frame_len, &spa, &aa,
&ies, &ies_len);
if (ret != 0)
return;
info = ft_info_find(ifindex, aa);
if (!info)
return;
if (!ft_parse_ies(info, hs, ies, ies_len))
goto ft_error;
info->parsed = true;
return;
ft_error:
l_debug("FT-over-DS authenticate to "MAC" failed", MAC_STR(info->aa));
}
static struct ft_info *ft_info_new(struct handshake_state *hs,
const struct scan_bss *target_bss)
{
struct ft_info *info = l_new(struct ft_info, 1);
info->ifindex = hs->ifindex;
memcpy(info->spa, hs->spa, 6);
memcpy(info->aa, target_bss->addr, 6);
memcpy(info->mde, target_bss->mde, sizeof(info->mde));
memcpy(info->prev_bssid, hs->aa, 6);
info->frequency = target_bss->frequency;
if (target_bss->rsne)
info->authenticator_ie = l_memdup(target_bss->rsne,
target_bss->rsne[1] + 2);
l_getrandom(info->snonce, 32);
return info;
}
static void ft_info_destroy(void *data)
{
struct ft_info *info = data;
if (info->fte)
l_free(info->fte);
if (info->authenticator_ie)
l_free(info->authenticator_ie);
l_free(info);
}
static void ft_prepare_handshake(struct ft_info *info,
struct handshake_state *hs)
{
if (!hs->supplicant_ie)
return;
memcpy(hs->snonce, info->snonce, sizeof(hs->snonce));
handshake_state_set_fte(hs, info->fte);
handshake_state_set_anonce(hs, info->ft_info.anonce);
handshake_state_set_kh_ids(hs, info->ft_info.r0khid,
info->ft_info.r0khid_len,
info->ft_info.r1khid);
handshake_state_derive_ptk(hs);
}
int ft_action(uint32_t ifindex, uint32_t freq, const struct scan_bss *target)
{
struct netdev *netdev = netdev_find(ifindex);
struct handshake_state *hs = netdev_get_handshake(netdev);
struct ft_info *info;
uint8_t ft_req[14];
struct iovec iov[5];
uint8_t ies[512];
size_t len;
int ret = -EINVAL;
info = ft_info_new(hs, target);
ft_req[0] = 6; /* FT category */
ft_req[1] = 1; /* FT Request action */
memcpy(ft_req + 2, info->spa, 6);
memcpy(ft_req + 8, info->aa, 6);
if (!ft_build_authenticate_ies(hs, hs->supplicant_ocvc, info->snonce,
ies, &len))
goto failed;
iov[0].iov_base = ft_req;
iov[0].iov_len = sizeof(ft_req);
iov[1].iov_base = ies;
iov[1].iov_len = len;
ret = tx_frame(hs->ifindex, 0x00d0, freq, hs->aa, iov, 2);
if (ret < 0)
goto failed;
l_queue_push_tail(info_list, info);
return 0;
failed:
l_free(info);
return ret;
}
int ft_associate(uint32_t ifindex, const uint8_t *addr)
{
struct netdev *netdev = netdev_find(ifindex);
struct handshake_state *hs = netdev_get_handshake(netdev);
struct ft_info *info;
int ret;
/*
* TODO: Since FT-over-DS is done early, before the time of roaming, it
* may end up that a completely new BSS is the best candidate and
* we haven't yet authenticated. We could actually authenticate
* at this point, but for now just assume the caller will choose
* a different BSS.
*/
info = ft_info_find(ifindex, addr);
if (!info)
return -ENOENT;
ft_prepare_handshake(info, hs);
ret = ft_tx_reassociate(ifindex, info->frequency, info->prev_bssid);
/* After this no previous auths will be valid */
ft_clear_authentications(ifindex);
return ret;
}
static bool remove_ifindex(void *data, void *user_data)
{
struct ft_info *info = data;
uint32_t ifindex = L_PTR_TO_UINT(user_data);
if (info->ifindex != ifindex)
return false;
ft_info_destroy(info);
return true;
}
void ft_clear_authentications(uint32_t ifindex)
{
l_queue_foreach_remove(info_list, remove_ifindex,
L_UINT_TO_PTR(ifindex));
}
static int ft_init(void)
{
info_list = l_queue_new();
return 0;
}
static void ft_exit(void)
{
if (!l_queue_isempty(info_list))
l_warn("stale FT info objects found!");
l_queue_destroy(info_list, ft_info_destroy);
}
IWD_MODULE(ft, ft_init, ft_exit);

View File

@ -21,11 +21,19 @@
*/
struct handshake_state;
struct scan_bss;
typedef int (*ft_tx_frame_func_t)(uint32_t ifindex, uint16_t frame_type,
uint32_t frequency,
const uint8_t *dest, struct iovec *iov,
size_t iov_len);
typedef void (*ft_tx_authenticate_func_t)(struct iovec *iov, size_t iov_len,
void *user_data);
typedef int (*ft_tx_associate_func_t)(struct iovec *ie_iov, size_t iov_len,
void *user_data);
typedef int (*ft_tx_associate_func_t)(uint32_t ifindex, uint32_t freq,
const uint8_t *prev_bssid,
struct iovec *ie_iov, size_t iov_len);
typedef int (*ft_get_oci)(void *user_data);
typedef void (*ft_ds_free_func_t)(void *user_data);
@ -71,3 +79,13 @@ struct auth_proto *ft_over_ds_sm_new(struct handshake_state *hs,
bool ft_over_ds_prepare_handshake(struct ft_ds_info *info,
struct handshake_state *hs);
void __ft_set_tx_frame_func(ft_tx_frame_func_t func);
void __ft_set_tx_associate_func(ft_tx_associate_func_t func);
int __ft_rx_associate(uint32_t ifindex, const uint8_t *frame,
size_t frame_len);
void __ft_rx_action(uint32_t ifindex, const uint8_t *frame, size_t frame_len);
void ft_clear_authentications(uint32_t ifindex);
int ft_action(uint32_t ifindex, uint32_t freq, const struct scan_bss *target);
int ft_associate(uint32_t ifindex, const uint8_t *addr);

View File

@ -4422,11 +4422,11 @@ restore_snonce:
MMPDU_STATUS_CODE_UNSPECIFIED);
}
static int netdev_ft_tx_associate(struct iovec *ft_iov, size_t n_ft_iov,
void *user_data)
static int netdev_ft_tx_associate(uint32_t ifindex, uint32_t freq,
const uint8_t *prev_bssid,
struct iovec *ft_iov, size_t n_ft_iov)
{
struct netdev *netdev = user_data;
struct auth_proto *ap = netdev->ap;
struct netdev *netdev = netdev_find(ifindex);
struct handshake_state *hs = netdev->handshake;
struct l_genl_msg *msg;
struct iovec iov[64];
@ -4447,7 +4447,7 @@ static int netdev_ft_tx_associate(struct iovec *ft_iov, size_t n_ft_iov,
mpdu_sort_ies(subtype, iov, c_iov);
l_genl_msg_append_attr(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN,
ap->prev_bssid);
prev_bssid);
l_genl_msg_append_attrv(msg, NL80211_ATTR_IE, iov, c_iov);
netdev->connect_cmd_id = l_genl_family_send(nl80211, msg,
@ -4659,17 +4659,15 @@ int netdev_fast_transition(struct netdev *netdev,
l_get_le16(target_bss->mde))
return -EINVAL;
prepare_ft(netdev, target_bss);
handshake_state_new_snonce(netdev->handshake);
netdev->connect_cb = cb;
netdev->ap = ft_over_air_sm_new(netdev->handshake,
netdev_ft_tx_authenticate,
netdev_ft_tx_associate,
netdev_get_oci, netdev);
memcpy(netdev->ap->prev_bssid, orig_bss->addr, ETH_ALEN);
prepare_ft(netdev, target_bss);
handshake_state_new_snonce(netdev->handshake);
wiphy_radio_work_insert(netdev->wiphy, &netdev->work,
WIPHY_WORK_PRIORITY_CONNECT, &ft_work_ops);
@ -4701,16 +4699,14 @@ int netdev_fast_transition_over_ds(struct netdev *netdev,
if (!info || !info->parsed)
return -ENOENT;
prepare_ft(netdev, target_bss);
ft_over_ds_prepare_handshake(&info->super, netdev->handshake);
netdev->connect_cb = cb;
netdev->ap = ft_over_ds_sm_new(netdev->handshake,
netdev_ft_tx_associate,
netdev);
memcpy(netdev->ap->prev_bssid, orig_bss->addr, ETH_ALEN);
prepare_ft(netdev, target_bss);
ft_over_ds_prepare_handshake(&info->super, netdev->handshake);
wiphy_radio_work_insert(netdev->wiphy, &netdev->work,
WIPHY_WORK_PRIORITY_CONNECT, &ft_work_ops);
@ -6656,6 +6652,8 @@ static int netdev_init(void)
__eapol_set_tx_packet_func(netdev_control_port_frame);
__eapol_set_install_pmk_func(netdev_set_pmk);
__ft_set_tx_associate_func(netdev_ft_tx_associate);
unicast_watch = l_genl_add_unicast_watch(genl, NL80211_GENL_NAME,
netdev_unicast_notify,
NULL, NULL);
@ -6720,3 +6718,4 @@ IWD_MODULE(netdev, netdev_init, netdev_exit);
IWD_MODULE_DEPENDS(netdev, eapol);
IWD_MODULE_DEPENDS(netdev, frame_xchg);
IWD_MODULE_DEPENDS(netdev, wiphy);
IWD_MODULE_DEPENDS(netdev, ft);