station/netdev: handle rekeying based on driver features

A new driver extended feature bit was added signifying if the driver
supports PTK replacement/rekeying. During a connect, netdev checks
for the driver feature and sets the handshakes 'no_rekey' flag
accordingly.

At some point the AP will decide to rekey which is handled inside
eapol. If no_rekey is unset we rekey as normal and the connection
remains open. If we have set no_rekey eapol will emit
HANDSHAKE_EVENT_REKEY_FAILED, which is now caught inside station. If
this happens our only choice is to fully disconnect and reconnect.
This commit is contained in:
James Prestwood 2019-01-25 11:23:12 -08:00 committed by Denis Kenzior
parent 8edaa23f8a
commit a2354f88a6
2 changed files with 46 additions and 1 deletions

View File

@ -2748,6 +2748,10 @@ static int netdev_connect_common(struct netdev *netdev,
handshake_state_set_authenticator_address(hs, bss->addr);
handshake_state_set_supplicant_address(hs, netdev->addr);
if (!wiphy_has_ext_feature(netdev->wiphy,
NL80211_EXT_FEATURE_CAN_REPLACE_PTK0))
handshake_state_set_no_rekey(hs, true);
if (netdev->sae_sm)
sae_start(netdev->sae_sm);
else if (netdev->owe)

View File

@ -377,6 +377,8 @@ void station_set_scan_results(struct station *station, struct l_queue *bss_list,
l_queue_destroy(old_bss_list, bss_free);
}
static void station_reconnect(struct station *station);
static void station_handshake_event(struct handshake_state *hs,
enum handshake_event event,
void *event_data, void *user_data)
@ -397,8 +399,11 @@ static void station_handshake_event(struct handshake_state *hs,
case HANDSHAKE_EVENT_FAILED:
netdev_handshake_failed(hs, l_get_u16(event_data));
break;
case HANDSHAKE_EVENT_SETTING_KEYS_FAILED:
case HANDSHAKE_EVENT_REKEY_FAILED:
station_reconnect(station);
break;
case HANDSHAKE_EVENT_COMPLETE:
case HANDSHAKE_EVENT_SETTING_KEYS_FAILED:
/*
* currently we dont care about any other events. The
* netdev_connect_cb will notify us when the connection is
@ -1811,6 +1816,42 @@ static struct l_dbus_message *station_dbus_connect_hidden_network(
return NULL;
}
static void station_disconnect_reconnect_cb(struct netdev *netdev, bool success,
void *user_data)
{
struct station *station = user_data;
struct handshake_state *hs;
int r;
hs = station_handshake_setup(station, station->connected_network,
station->connected_bss);
if (!hs)
goto error;
r = netdev_connect(station->netdev, station->connected_bss, hs,
station_netdev_event, station_connect_cb,
station);
if (r < 0)
goto error;
return;
error:
station_disconnect(station);
}
static void station_reconnect(struct station *station)
{
/*
* Rather than doing 4 or so state changes, lets just go into
* roaming for the duration of this reconnect.
*/
station_enter_state(station, STATION_STATE_ROAMING);
netdev_disconnect(station->netdev, station_disconnect_reconnect_cb,
station);
}
static void station_disconnect_cb(struct netdev *netdev, bool success,
void *user_data)
{