netdev: process association in netdev_associate_event
Apart from OWE, the association event was disregarded and all association processing was done in netdev_connect_event. This led to netdev_connect_event having to handle all the logic of both success and failure, as well as parsing the association for FT and OWE. Also, without checking the status code in the associate frame there is the potential for the kernel to think we are connected even if association failed (e.g. rogue AP). This change introduces two flags into netdev, expect_connect_failure and ignore_connect_event. All the FT processing that was once in netdev_connect_event has now been moved into netdev_associate_event, as well as non-FT associate frame processing. The connect event now only handles failure cases for soft/half MAC cards. Note: Since fullmac cards rely on the connect event, the eapol_start and netdev_connect_ok were left in netdev_connect_event. Since neither auth/assoc events come in on fullmac we shouldn't have any conflict with the new flags. Once a connection has completed association, EAPoL is started from netdev_associate_event (if required) and the ignore_connect_event flag can be set. This will bypass the connect event. If a connection has failed during association for whatever reason, we can set expect_connect_failure, the netdev reason, and the MPDU status code. This allows netdev_connect_event to both handle the error, and, if required, send a deauth telling the kernel that we have failed (protecting against the rogue AP situation).
This commit is contained in:
parent
5027bd3d0b
commit
e3f4bfb428
187
src/netdev.c
187
src/netdev.c
|
@ -135,6 +135,8 @@ struct netdev {
|
||||||
bool in_ft : 1;
|
bool in_ft : 1;
|
||||||
bool cur_rssi_low : 1;
|
bool cur_rssi_low : 1;
|
||||||
bool use_4addr : 1;
|
bool use_4addr : 1;
|
||||||
|
bool ignore_connect_event : 1;
|
||||||
|
bool expect_connect_failure : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct netdev_preauth_state {
|
struct netdev_preauth_state {
|
||||||
|
@ -584,6 +586,8 @@ static void netdev_connect_free(struct netdev *netdev)
|
||||||
netdev->result = NETDEV_RESULT_OK;
|
netdev->result = NETDEV_RESULT_OK;
|
||||||
netdev->last_code = 0;
|
netdev->last_code = 0;
|
||||||
netdev->in_ft = false;
|
netdev->in_ft = false;
|
||||||
|
netdev->ignore_connect_event = false;
|
||||||
|
netdev->expect_connect_failure = false;
|
||||||
|
|
||||||
netdev_rssi_polling_update(netdev);
|
netdev_rssi_polling_update(netdev);
|
||||||
|
|
||||||
|
@ -1575,13 +1579,22 @@ static void netdev_set_rekey_offload(uint32_t ifindex,
|
||||||
* FT initial Mobility Domain association (12.4) or a Fast Transition
|
* FT initial Mobility Domain association (12.4) or a Fast Transition
|
||||||
* (12.8.5).
|
* (12.8.5).
|
||||||
*/
|
*/
|
||||||
static bool netdev_handle_associate_resp_ies(struct handshake_state *hs,
|
static bool netdev_ft_process_associate(struct netdev *netdev,
|
||||||
const uint8_t *rsne, const uint8_t *mde,
|
const uint8_t *frame, size_t frame_len,
|
||||||
const uint8_t *fte, bool transition)
|
uint16_t *out_status)
|
||||||
{
|
{
|
||||||
|
struct handshake_state *hs = netdev->handshake;
|
||||||
|
const uint8_t *rsne = NULL;
|
||||||
|
const uint8_t *mde = NULL;
|
||||||
|
const uint8_t *fte = NULL;
|
||||||
|
bool transition = netdev->in_ft;
|
||||||
const uint8_t *sent_mde = hs->mde;
|
const uint8_t *sent_mde = hs->mde;
|
||||||
bool is_rsn = hs->supplicant_ie != NULL;
|
bool is_rsn = hs->supplicant_ie != NULL;
|
||||||
|
|
||||||
|
if (!ft_parse_associate_resp_frame(frame, frame_len, out_status, &rsne,
|
||||||
|
&mde, &fte))
|
||||||
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* During a transition in an RSN, check for an RSNE containing the
|
* During a transition in an RSN, check for an RSNE containing the
|
||||||
* PMK-R1-Name and the remaining fields same as in the advertised
|
* PMK-R1-Name and the remaining fields same as in the advertised
|
||||||
|
@ -1731,15 +1744,10 @@ static void netdev_connect_event(struct l_genl_msg *msg, struct netdev *netdev)
|
||||||
uint16_t type, len;
|
uint16_t type, len;
|
||||||
const void *data;
|
const void *data;
|
||||||
const uint16_t *status_code = NULL;
|
const uint16_t *status_code = NULL;
|
||||||
const uint8_t *ies = NULL;
|
|
||||||
size_t ies_len;
|
|
||||||
const uint8_t *rsne = NULL;
|
|
||||||
const uint8_t *mde = NULL;
|
|
||||||
const uint8_t *fte = NULL;
|
|
||||||
|
|
||||||
l_debug("");
|
l_debug("");
|
||||||
|
|
||||||
if (netdev->owe)
|
if (netdev->ignore_connect_event)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!netdev->connected) {
|
if (!netdev->connected) {
|
||||||
|
@ -1761,55 +1769,27 @@ static void netdev_connect_event(struct l_genl_msg *msg, struct netdev *netdev)
|
||||||
case NL80211_ATTR_STATUS_CODE:
|
case NL80211_ATTR_STATUS_CODE:
|
||||||
if (len == sizeof(uint16_t))
|
if (len == sizeof(uint16_t))
|
||||||
status_code = data;
|
status_code = data;
|
||||||
|
|
||||||
break;
|
|
||||||
case NL80211_ATTR_RESP_IE:
|
|
||||||
ies = data;
|
|
||||||
ies_len = len;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (netdev->expect_connect_failure) {
|
||||||
|
/*
|
||||||
|
* The kernel may think we are connected when we are actually
|
||||||
|
* expecting a failure here, e.g. if Authenticate/Associate had
|
||||||
|
* previously failed. If so we need to deauth to let the kernel
|
||||||
|
* know.
|
||||||
|
*/
|
||||||
|
if (status_code && *status_code == 0)
|
||||||
|
goto deauth;
|
||||||
|
else
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
/* AP Rejected the authenticate / associate */
|
/* AP Rejected the authenticate / associate */
|
||||||
if (!status_code || *status_code != 0)
|
if (!status_code || *status_code != 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
/* Check 802.11r IEs */
|
|
||||||
if (ies) {
|
|
||||||
struct ie_tlv_iter iter;
|
|
||||||
|
|
||||||
ie_tlv_iter_init(&iter, ies, ies_len);
|
|
||||||
|
|
||||||
while (ie_tlv_iter_next(&iter)) {
|
|
||||||
switch (ie_tlv_iter_get_tag(&iter)) {
|
|
||||||
case IE_TYPE_RSN:
|
|
||||||
if (rsne)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
rsne = ie_tlv_iter_get_data(&iter) - 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IE_TYPE_MOBILITY_DOMAIN:
|
|
||||||
if (mde)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
mde = ie_tlv_iter_get_data(&iter) - 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IE_TYPE_FAST_BSS_TRANSITION:
|
|
||||||
if (fte)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
fte = ie_tlv_iter_get_data(&iter) - 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!netdev_handle_associate_resp_ies(netdev->handshake, rsne, mde, fte,
|
|
||||||
netdev->in_ft))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (netdev->sm) {
|
if (netdev->sm) {
|
||||||
/*
|
/*
|
||||||
* Start processing EAPoL frames now that the state machine
|
* Start processing EAPoL frames now that the state machine
|
||||||
|
@ -1818,28 +1798,26 @@ static void netdev_connect_event(struct l_genl_msg *msg, struct netdev *netdev)
|
||||||
if (!eapol_start(netdev->sm))
|
if (!eapol_start(netdev->sm))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (!netdev->in_ft)
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (netdev->in_ft) {
|
|
||||||
bool is_rsn = netdev->handshake->supplicant_ie != NULL;
|
|
||||||
|
|
||||||
netdev->in_ft = false;
|
|
||||||
|
|
||||||
if (is_rsn) {
|
|
||||||
handshake_state_install_ptk(netdev->handshake);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
netdev_connect_ok(netdev);
|
netdev_connect_ok(netdev);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
|
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
|
||||||
*status_code);
|
(status_code) ? *status_code :
|
||||||
|
MMPDU_STATUS_CODE_UNSPECIFIED);
|
||||||
|
return;
|
||||||
|
|
||||||
|
deauth:
|
||||||
|
msg = netdev_build_cmd_deauthenticate(netdev,
|
||||||
|
MMPDU_REASON_CODE_UNSPECIFIED);
|
||||||
|
netdev->disconnect_cmd_id = l_genl_family_send(nl80211,
|
||||||
|
msg,
|
||||||
|
netdev_disconnect_cb,
|
||||||
|
netdev, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int ie_rsn_akm_suite_to_nl80211(enum ie_rsn_akm_suite akm)
|
static unsigned int ie_rsn_akm_suite_to_nl80211(enum ie_rsn_akm_suite akm)
|
||||||
|
@ -2069,8 +2047,8 @@ static void netdev_cmd_ft_reassociate_cb(struct l_genl_msg *msg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void netdev_ft_process(struct netdev *netdev, const uint8_t *frame,
|
static void netdev_ft_process_authenticate(struct netdev *netdev,
|
||||||
size_t frame_len)
|
const uint8_t *frame, size_t frame_len)
|
||||||
{
|
{
|
||||||
struct l_genl_msg *cmd_associate, *cmd_deauth;
|
struct l_genl_msg *cmd_associate, *cmd_deauth;
|
||||||
uint16_t status_code;
|
uint16_t status_code;
|
||||||
|
@ -2324,7 +2302,7 @@ static void netdev_authenticate_event(struct l_genl_msg *msg,
|
||||||
goto auth_error;
|
goto auth_error;
|
||||||
|
|
||||||
if (netdev->in_ft)
|
if (netdev->in_ft)
|
||||||
netdev_ft_process(netdev, frame, frame_len);
|
netdev_ft_process_authenticate(netdev, frame, frame_len);
|
||||||
else if (netdev->sae_sm)
|
else if (netdev->sae_sm)
|
||||||
netdev_sae_process(netdev,
|
netdev_sae_process(netdev,
|
||||||
((struct mmpdu_header *)frame)->address_2,
|
((struct mmpdu_header *)frame)->address_2,
|
||||||
|
@ -2348,13 +2326,11 @@ static void netdev_associate_event(struct l_genl_msg *msg,
|
||||||
uint16_t type, len;
|
uint16_t type, len;
|
||||||
const void *data;
|
const void *data;
|
||||||
size_t frame_len = 0;
|
size_t frame_len = 0;
|
||||||
const uint8_t *frame;
|
const uint8_t *frame = NULL;
|
||||||
|
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
|
||||||
|
|
||||||
l_debug("");
|
l_debug("");
|
||||||
|
|
||||||
if (!netdev->owe)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!l_genl_attr_init(&attr, msg)) {
|
if (!l_genl_attr_init(&attr, msg)) {
|
||||||
l_debug("attr init failed");
|
l_debug("attr init failed");
|
||||||
return;
|
return;
|
||||||
|
@ -2370,14 +2346,59 @@ static void netdev_associate_event(struct l_genl_msg *msg,
|
||||||
frame = data;
|
frame = data;
|
||||||
frame_len = len;
|
frame_len = len;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!frame)
|
||||||
|
goto assoc_failed;
|
||||||
|
|
||||||
|
if (netdev->owe) {
|
||||||
owe_rx_associate(netdev->owe, frame, frame_len);
|
owe_rx_associate(netdev->owe, frame, frame_len);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!netdev_ft_process_associate(netdev, frame, frame_len,
|
||||||
|
&status_code))
|
||||||
|
goto assoc_failed;
|
||||||
|
|
||||||
|
if (status_code != 0)
|
||||||
|
goto assoc_failed;
|
||||||
|
|
||||||
|
/* Connection can be fully handled here, not in connect event */
|
||||||
|
netdev->ignore_connect_event = true;
|
||||||
|
|
||||||
|
if (netdev->sm) {
|
||||||
|
/*
|
||||||
|
* Start processing EAPoL frames now that the state machine
|
||||||
|
* has all the input data even in FT mode.
|
||||||
|
*/
|
||||||
|
if (!eapol_start(netdev->sm))
|
||||||
|
goto assoc_failed;
|
||||||
|
|
||||||
|
if (!netdev->in_ft)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (netdev->in_ft) {
|
||||||
|
bool is_rsn = netdev->handshake->supplicant_ie != NULL;
|
||||||
|
|
||||||
|
netdev->in_ft = false;
|
||||||
|
|
||||||
|
if (is_rsn) {
|
||||||
|
handshake_state_install_ptk(netdev->handshake);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
netdev_connect_ok(netdev);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
assoc_failed:
|
assoc_failed:
|
||||||
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
|
netdev->result = NETDEV_RESULT_ASSOCIATION_FAILED;
|
||||||
MMPDU_STATUS_CODE_UNSPECIFIED);
|
netdev->last_code = status_code;
|
||||||
|
netdev->expect_connect_failure = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void netdev_cmd_connect_cb(struct l_genl_msg *msg, void *user_data)
|
static void netdev_cmd_connect_cb(struct l_genl_msg *msg, void *user_data)
|
||||||
|
@ -2580,15 +2601,23 @@ static void netdev_owe_complete(uint16_t status, void *user_data)
|
||||||
{
|
{
|
||||||
struct netdev *netdev = user_data;
|
struct netdev *netdev = user_data;
|
||||||
|
|
||||||
if (status) {
|
switch (status) {
|
||||||
/*
|
case 0: /* success */
|
||||||
* OWE will never fail during authenticate, at least internally,
|
break;
|
||||||
* so we can always assume its association that failed.
|
case MMPDU_STATUS_CODE_UNSUPP_FINITE_CYCLIC_GROUP:
|
||||||
*/
|
if (owe_retry(netdev->owe)) {
|
||||||
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
|
netdev->ignore_connect_event = true;
|
||||||
status);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
/* fall through */
|
||||||
|
default:
|
||||||
|
netdev->result = NETDEV_RESULT_ASSOCIATION_FAILED;
|
||||||
|
netdev->last_code = status;
|
||||||
|
netdev->expect_connect_failure = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
netdev->ignore_connect_event = true;
|
||||||
|
|
||||||
netdev->sm = eapol_sm_new(netdev->handshake);
|
netdev->sm = eapol_sm_new(netdev->handshake);
|
||||||
eapol_register(netdev->sm);
|
eapol_register(netdev->sm);
|
||||||
|
|
26
src/owe.c
26
src/owe.c
|
@ -245,21 +245,10 @@ void owe_rx_associate(struct owe_sm *owe, const uint8_t *frame, size_t len)
|
||||||
body = mmpdu_body(mpdu);
|
body = mmpdu_body(mpdu);
|
||||||
|
|
||||||
if (body->status_code == MMPDU_STATUS_CODE_UNSUPP_FINITE_CYCLIC_GROUP) {
|
if (body->status_code == MMPDU_STATUS_CODE_UNSUPP_FINITE_CYCLIC_GROUP) {
|
||||||
/* retry with another group, if possible */
|
|
||||||
owe->retry++;
|
|
||||||
|
|
||||||
if (!owe_reset(owe)) {
|
|
||||||
owe->complete(body->status_code, owe->user_data);
|
owe->complete(body->status_code, owe->user_data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
l_debug("OWE retrying with group %u", owe->group);
|
|
||||||
|
|
||||||
owe_start(owe);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ie_tlv_iter_init(&iter, body->ies, (const uint8_t *) mpdu + len -
|
ie_tlv_iter_init(&iter, body->ies, (const uint8_t *) mpdu + len -
|
||||||
body->ies);
|
body->ies);
|
||||||
|
|
||||||
|
@ -323,3 +312,18 @@ void owe_rx_associate(struct owe_sm *owe, const uint8_t *frame, size_t len)
|
||||||
owe_failed:
|
owe_failed:
|
||||||
owe->complete(MMPDU_REASON_CODE_UNSPECIFIED, owe->user_data);
|
owe->complete(MMPDU_REASON_CODE_UNSPECIFIED, owe->user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool owe_retry(struct owe_sm *owe)
|
||||||
|
{
|
||||||
|
/* retry with another group, if possible */
|
||||||
|
owe->retry++;
|
||||||
|
|
||||||
|
if (!owe_reset(owe))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
l_debug("OWE retrying with group %u", owe->group);
|
||||||
|
|
||||||
|
owe_start(owe);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -35,5 +35,6 @@ struct owe_sm *owe_sm_new(struct handshake_state *hs,
|
||||||
void owe_sm_free(struct owe_sm *owe);
|
void owe_sm_free(struct owe_sm *owe);
|
||||||
|
|
||||||
void owe_start(struct owe_sm *owe);
|
void owe_start(struct owe_sm *owe);
|
||||||
|
bool owe_retry(struct owe_sm *owe);
|
||||||
void owe_rx_authenticate(struct owe_sm *owe);
|
void owe_rx_authenticate(struct owe_sm *owe);
|
||||||
void owe_rx_associate(struct owe_sm *owe, const uint8_t *frame, size_t len);
|
void owe_rx_associate(struct owe_sm *owe, const uint8_t *frame, size_t len);
|
||||||
|
|
Loading…
Reference in New Issue