@@ -479,48 +479,6 @@ ft_error:
return false;
}
-static int ft_process_ies(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;
-
- /* Check 802.11r IEs */
- if (!ies)
- goto ft_error;
-
- if (parse_ies(hs, hs->authenticator_ie, ies, ies_len,
- &mde, &fte) < 0)
- goto ft_error;
-
- if (!mde_equal(hs->mde, mde))
- goto ft_error;
-
- if (is_rsn) {
- struct ie_ft_info ft_info;
-
- if (!ft_parse_fte(hs, hs->snonce, fte, &ft_info))
- goto ft_error;
-
- handshake_state_set_fte(hs, fte);
-
- handshake_state_set_anonce(hs, ft_info.anonce);
-
- handshake_state_set_kh_ids(hs, ft_info.r0khid,
- ft_info.r0khid_len,
- ft_info.r1khid);
-
- handshake_state_derive_ptk(hs);
- } else if (fte)
- goto ft_error;
-
- return 0;
-
-ft_error:
- return -EBADMSG;
-}
-
int ft_over_ds_parse_action_response(const uint8_t *frame, size_t frame_len,
const uint8_t **spa_out,
const uint8_t **aa_out,
@@ -597,40 +555,6 @@ void ft_info_free(struct ft_info *info)
destroy(info);
}
-static int ft_rx_authenticate(struct auth_proto *ap, const uint8_t *frame,
- size_t frame_len)
-{
- struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
- uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
- const uint8_t *ies = NULL;
- size_t ies_len;
- int ret;
-
- /*
- * Parse the Authentication Response and validate the contents
- * according to 12.5.2 / 12.5.4: RSN or non-RSN Over-the-air
- * FT Protocol.
- */
- if (!ft_parse_authentication_resp_frame(frame, frame_len, ft->hs->spa,
- ft->hs->aa, ft->hs->aa,
- 2, &status_code,
- &ies, &ies_len))
- goto auth_error;
-
- /* AP Rejected the authenticate / associate */
- if (status_code != 0)
- goto auth_error;
-
- ret = ft_process_ies(ft->hs, ies, ies_len);
- if (ret < 0)
- goto auth_error;
-
- return ft->get_oci(ft->user_data);
-
-auth_error:
- return (int)status_code;
-}
-
static int ft_rx_associate(struct auth_proto *ap, const uint8_t *frame,
size_t frame_len)
{
@@ -768,13 +692,6 @@ static int ft_rx_associate(struct auth_proto *ap, const uint8_t *frame,
return 0;
}
-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);
-}
-
static void ft_sm_free(struct auth_proto *ap)
{
struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
@@ -863,41 +780,23 @@ bool ft_build_authenticate_ies(struct handshake_state *hs,
static bool ft_start(struct auth_proto *ap)
{
struct ft_sm *ft = l_container_of(ap, struct ft_sm, ap);
- struct handshake_state *hs = ft->hs;
- struct iovec iov;
- uint8_t buf[512];
- size_t len;
- if (!ft_build_authenticate_ies(hs, hs->snonce, buf, &len))
- return false;
-
- iov.iov_base = buf;
- iov.iov_len = len;
-
- ft->tx_auth(&iov, 1, ft->user_data);
-
- return true;
+ return ft_tx_reassociate(ft) == 0;
}
struct auth_proto *ft_over_air_sm_new(struct handshake_state *hs,
- ft_tx_authenticate_func_t tx_auth,
ft_tx_associate_func_t tx_assoc,
- ft_get_oci get_oci,
void *user_data)
{
struct ft_sm *ft = l_new(struct ft_sm, 1);
- ft->tx_auth = tx_auth;
ft->tx_assoc = tx_assoc;
- ft->get_oci = get_oci;
ft->hs = hs;
ft->user_data = user_data;
- ft->ap.rx_authenticate = ft_rx_authenticate;
ft->ap.rx_associate = ft_rx_associate;
ft->ap.start = ft_start;
ft->ap.free = ft_sm_free;
- ft->ap.rx_oci = ft_rx_oci;
return &ft->ap;
}
@@ -61,9 +61,7 @@ bool ft_parse_ies(struct ft_info *info, struct handshake_state *hs,
const uint8_t *ies, size_t ies_len);
struct auth_proto *ft_over_air_sm_new(struct handshake_state *hs,
- ft_tx_authenticate_func_t tx_auth,
ft_tx_associate_func_t tx_assoc,
- ft_get_oci get_oci,
void *user_data);
struct auth_proto *ft_over_ds_sm_new(struct handshake_state *hs,
@@ -99,6 +99,7 @@ struct netdev_handshake_state {
struct netdev_ft_info {
struct ft_info super;
struct netdev *netdev;
+ uint32_t frequency;
bool parsed : 1;
};
@@ -138,6 +139,7 @@ struct netdev {
uint32_t qos_map_cmd_id;
uint32_t mac_change_cmd_id;
uint32_t get_oci_cmd_id;
+ uint32_t ft_auth_cmd_id;
enum netdev_result result;
uint16_t last_code; /* reason or status, depending on result */
struct l_timeout *neighbor_report_timeout;
@@ -848,6 +850,11 @@ static void netdev_connect_free(struct netdev *netdev)
netdev->get_oci_cmd_id = 0;
}
+ if (netdev->ft_auth_cmd_id) {
+ frame_xchg_cancel(netdev->ft_auth_cmd_id);
+ netdev->ft_auth_cmd_id = 0;
+ }
+
if (netdev->ft_list) {
l_queue_destroy(netdev->ft_list, netdev_ft_entry_free);
netdev->ft_list = NULL;
@@ -4359,48 +4366,6 @@ static uint32_t netdev_send_action_frame(struct netdev *netdev,
user_data);
}
-static void netdev_cmd_authenticate_ft_cb(struct l_genl_msg *msg,
- void *user_data)
-{
- struct netdev *netdev = user_data;
-
- netdev->connect_cmd_id = 0;
-
- if (l_genl_msg_get_error(msg) < 0)
- netdev_connect_failed(netdev,
- NETDEV_RESULT_AUTHENTICATION_FAILED,
- MMPDU_STATUS_CODE_UNSPECIFIED);
-}
-
-static void netdev_ft_tx_authenticate(struct iovec *iov,
- size_t iov_len, void *user_data)
-{
- struct netdev *netdev = user_data;
- struct l_genl_msg *cmd_authenticate;
-
- cmd_authenticate = netdev_build_cmd_authenticate(netdev,
- NL80211_AUTHTYPE_FT);
- l_genl_msg_append_attrv(cmd_authenticate, NL80211_ATTR_IE, iov,
- iov_len);
-
- netdev->connect_cmd_id = l_genl_family_send(nl80211,
- cmd_authenticate,
- netdev_cmd_authenticate_ft_cb,
- netdev, NULL);
- if (!netdev->connect_cmd_id) {
- l_genl_msg_unref(cmd_authenticate);
- goto restore_snonce;
- }
-
- return;
-
-restore_snonce:
- memcpy(netdev->handshake->snonce, netdev->prev_snonce, 32);
-
- netdev_connect_failed(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED,
- MMPDU_STATUS_CODE_UNSPECIFIED);
-}
-
static int netdev_ft_tx_associate(struct iovec *ft_iov, size_t n_ft_iov,
void *user_data)
{
@@ -4441,7 +4406,7 @@ static int netdev_ft_tx_associate(struct iovec *ft_iov, size_t n_ft_iov,
return 0;
}
-static void prepare_ft(struct netdev *netdev, const struct scan_bss *target_bss)
+static void prepare_ft(struct netdev *netdev, struct netdev_ft_info *info)
{
struct netdev_handshake_state *nhs;
@@ -4452,15 +4417,15 @@ static void prepare_ft(struct netdev *netdev, const struct scan_bss *target_bss)
*/
memcpy(netdev->prev_snonce, netdev->handshake->snonce, 32);
- netdev->frequency = target_bss->frequency;
+ netdev->frequency = info->frequency;
handshake_state_set_authenticator_address(netdev->handshake,
- target_bss->addr);
+ info->super.aa);
- if (target_bss->rsne)
+ if (info->super.authenticator_ie)
handshake_state_set_authenticator_ie(netdev->handshake,
- target_bss->rsne);
- memcpy(netdev->handshake->mde + 2, target_bss->mde, 3);
+ info->super.authenticator_ie);
+ memcpy(netdev->handshake->mde + 2, info->super.mde, 3);
netdev->handshake->active_tk_index = 0;
netdev->associated = false;
@@ -4506,6 +4471,8 @@ static void prepare_ft(struct netdev *netdev, const struct scan_bss *target_bss)
eapol_sm_free(netdev->sm);
netdev->sm = NULL;
}
+
+ ft_prepare_handshake(&info->super, netdev->handshake);
}
static void netdev_ft_over_ds_auth_failed(struct netdev_ft_info *info,
@@ -4624,11 +4591,149 @@ static const struct wiphy_radio_work_item_ops ft_work_ops = {
.do_work = netdev_ft_work_ready,
};
+static void netdev_authenticate_response_ft_cb(const struct mmpdu_header *hdr,
+ const void *body,
+ size_t body_len, int rssi,
+ void *user_data)
+{
+ struct netdev_ft_info *info = user_data;
+ struct netdev *netdev = info->netdev;
+ uint16_t status;
+ const uint8_t *ies;
+ size_t ies_len;
+
+ if (!ft_parse_authentication_resp_frame((const uint8_t *)hdr,
+ mmpdu_header_len(hdr) + body_len,
+ info->super.spa, info->super.aa,
+ info->super.aa, 2, &status,
+ &ies, &ies_len))
+ goto failed;
+
+ if (status != 0)
+ goto failed;
+
+ if (!ft_parse_ies(&info->super, netdev->handshake, ies, ies_len))
+ goto failed;
+
+ /* If we got here authentication was successful, continue with FT */
+ netdev->ap = ft_over_air_sm_new(netdev->handshake,
+ netdev_ft_tx_associate,
+ netdev);
+
+ memcpy(netdev->ap->prev_bssid, netdev->handshake->aa, ETH_ALEN);
+
+ prepare_ft(netdev, info);
+
+ wiphy_radio_work_insert(netdev->wiphy, &netdev->work,
+ WIPHY_WORK_PRIORITY_CONNECT, &ft_work_ops);
+
+ return;
+
+failed:
+ netdev_connect_failed(netdev, NETDEV_RESULT_ABORTED,
+ MMPDU_STATUS_CODE_UNSPECIFIED);
+}
+
+static const struct frame_xchg_prefix ft_prefix = {
+ .frame_type = 0x0000 | (MPDU_MANAGEMENT_SUBTYPE_AUTHENTICATION << 4),
+ .data = (uint8_t []) { 0x02, 0x00 },
+ .len = 2,
+};
+
+static void netdev_cmd_authenticate_ft_cb(int err, void *user_data)
+{
+ struct netdev_ft_info *info = user_data;
+
+ if (err < 0)
+ netdev_connect_failed(info->netdev,
+ NETDEV_RESULT_AUTHENTICATION_FAILED,
+}
+
+static bool netdev_send_ft_authenticate(struct netdev *netdev,
+ struct netdev_ft_info *info)
+{
+ uint8_t header[28 + sizeof(struct mmpdu_authentication)];
+ uint8_t ies[256];
+ size_t len;
+ struct iovec iov[3];
+ struct mmpdu_header *mpdu = (struct mmpdu_header *) header;
+ struct mmpdu_authentication *auth;
+ struct handshake_state *hs = netdev->handshake;
+
+ memset(mpdu, 0, sizeof(*mpdu));
+
+ /* Header */
+ mpdu->fc.protocol_version = 0;
+ mpdu->fc.type = MPDU_TYPE_MANAGEMENT;
+ mpdu->fc.subtype = MPDU_MANAGEMENT_SUBTYPE_AUTHENTICATION;
+ memcpy(mpdu->address_1, info->super.aa, 6);
+ memcpy(mpdu->address_2, netdev->addr, 6);
+ memcpy(mpdu->address_3, info->super.aa, 6);
+
+ /* Authentication body */
+ auth = (void *) mmpdu_body(mpdu);
+ auth->algorithm = L_CPU_TO_LE16(MMPDU_AUTH_ALGO_FT);
+ auth->transaction_sequence = L_CPU_TO_LE16(1);
+ auth->status = L_CPU_TO_LE16(0);
+
+ iov[0].iov_base = mpdu;
+ iov[0].iov_len = mmpdu_header_len(mpdu) +
+ sizeof(struct mmpdu_authentication);
+
+ if (!ft_build_authenticate_ies(hs, info->super.snonce, ies, &len))
+ return false;
+
+ iov[1].iov_base = ies;
+ iov[1].iov_len = len;
+
+ iov[2].iov_base = NULL;
+
+ netdev->ft_auth_cmd_id = frame_xchg_start(netdev->wdev_id, iov,
+ info->frequency, 0, 100, 0, 0,
+ netdev_cmd_authenticate_ft_cb, info, NULL,
+ &ft_prefix, netdev_authenticate_response_ft_cb, NULL);
+
+ return netdev->ft_auth_cmd_id != 0;
+}
+
+static void netdev_ft_info_free(struct ft_info *ft)
+{
+ struct netdev_ft_info *info = l_container_of(ft,
+ struct netdev_ft_info, super);
+
+ l_free(info);
+}
+
+static struct netdev_ft_info *netdev_ft_info_new(struct netdev *netdev,
+ const struct scan_bss *bss)
+{
+ struct handshake_state *hs = netdev->handshake;
+ struct netdev_ft_info *info;
+
+ info = l_new(struct netdev_ft_info, 1);
+ info->netdev = netdev;
+ info->frequency = bss->frequency;
+
+ memcpy(info->super.spa, hs->spa, ETH_ALEN);
+ memcpy(info->super.aa, bss->addr, ETH_ALEN);
+ memcpy(info->super.mde, bss->mde, sizeof(info->super.mde));
+
+ if (bss->rsne)
+ info->super.authenticator_ie = l_memdup(bss->rsne,
+ bss->rsne[1] + 2);
+ l_getrandom(info->super.snonce, 32);
+ info->super.free = netdev_ft_info_free;
+
+ return info;
+}
+
int netdev_fast_transition(struct netdev *netdev,
const struct scan_bss *target_bss,
const struct scan_bss *orig_bss,
netdev_connect_cb_t cb)
{
+ struct netdev_ft_info *info;
+
if (!netdev->operational)
return -ENOTCONN;
@@ -4637,20 +4742,14 @@ 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);
+ info = netdev_ft_info_new(netdev, target_bss);
- wiphy_radio_work_insert(netdev->wiphy, &netdev->work,
- WIPHY_WORK_PRIORITY_CONNECT, &ft_work_ops);
+ if (!netdev_send_ft_authenticate(netdev, info)) {
+ netdev_ft_entry_free(info);
+ return -EBADMSG;
+ }
return 0;
}
@@ -4679,9 +4778,7 @@ int netdev_fast_transition_over_ds(struct netdev *netdev,
if (!info || !info->parsed)
return -ENOENT;
- prepare_ft(netdev, target_bss);
-
- ft_prepare_handshake(&info->super, netdev->handshake);
+ prepare_ft(netdev, info);
netdev->connect_cb = cb;
@@ -4707,14 +4804,6 @@ static void netdev_ft_request_cb(struct l_genl_msg *msg, void *user_data)
}
}
-static void netdev_ft_info_free(struct ft_info *ft)
-{
- struct netdev_ft_info *info = l_container_of(ft,
- struct netdev_ft_info, super);
-
- l_free(info);
-}
-
int netdev_fast_transition_over_ds_action(struct netdev *netdev,
const struct scan_bss *target_bss)
{
@@ -4735,19 +4824,7 @@ int netdev_fast_transition_over_ds_action(struct netdev *netdev,
l_debug("");
- info = l_new(struct netdev_ft_info, 1);
- info->netdev = netdev;
-
- memcpy(info->super.spa, hs->spa, ETH_ALEN);
- memcpy(info->super.aa, target_bss->addr, ETH_ALEN);
- memcpy(info->super.mde, target_bss->mde, sizeof(info->super.mde));
-
- if (target_bss->rsne)
- info->super.authenticator_ie = l_memdup(target_bss->rsne,
- target_bss->rsne[1] + 2);
-
- l_getrandom(info->super.snonce, 32);
- info->super.free = netdev_ft_info_free;
+ info = netdev_ft_info_new(netdev, target_bss);
ft_req[0] = 6; /* FT category */
ft_req[1] = 1; /* FT Request action */