netdev: added sae functionality to netdev

In order to plug SAE into the existing connect mechanism the actual
CMD_CONNECT message is never sent, rather sae_register takes care
of sending out CMD_AUTHENTICATE. This required some shuffling of
code in order to handle both eapol and sae. In the case of non-SAE
authentication everything behaves as it did before. When using SAE
an sae_sm is created when a connection is attempted but the eapol_sm
is not. After SAE succeeds it will start association and then create
the eapol_sm and start the 4-way handshake.

This change also adds the handshake SAE events to device and
initializes SAE in main.
This commit is contained in:
James Prestwood 2018-08-13 16:25:48 -07:00 committed by Denis Kenzior
parent 220fb61128
commit fd8671e9c5
2 changed files with 201 additions and 67 deletions

View File

@ -779,10 +779,15 @@ static struct handshake_state *device_handshake_setup(struct device *device,
handshake_state_set_own_wpa(hs, rsne_buf);
}
if (security == SECURITY_PSK)
handshake_state_set_pmk(hs, network_get_psk(network),
32);
else
if (security == SECURITY_PSK) {
/* SAE will generate/set the PMK */
if (info.akm_suites == IE_RSN_AKM_SUITE_SAE_SHA256)
handshake_state_set_passphrase(hs,
network_get_passphrase(network));
else
handshake_state_set_pmk(hs,
network_get_psk(network), 32);
} else
handshake_state_set_8021x_config(hs,
network_get_settings(network));

View File

@ -54,6 +54,7 @@
#include "src/ftutil.h"
#include "src/util.h"
#include "src/watchlist.h"
#include "src/sae.h"
#ifndef ENOTSUPP
#define ENOTSUPP 524
@ -85,6 +86,7 @@ struct netdev {
netdev_adhoc_cb_t adhoc_cb;
void *user_data;
struct eapol_sm *sm;
struct sae_sm *sae_sm;
struct handshake_state *handshake;
uint32_t connect_cmd_id;
uint32_t disconnect_cmd_id;
@ -507,6 +509,11 @@ static void netdev_connect_free(struct netdev *netdev)
netdev->sm = NULL;
}
if (netdev->sae_sm) {
sae_sm_free(netdev->sae_sm);
netdev->sae_sm = NULL;
}
eapol_preauth_cancel(netdev->index);
if (netdev->handshake) {
@ -1962,68 +1969,19 @@ static void netdev_cmd_ft_reassociate_cb(struct l_genl_msg *msg,
}
}
static void netdev_authenticate_event(struct l_genl_msg *msg,
struct netdev *netdev)
static void netdev_ft_process(struct netdev *netdev, const uint8_t *frame,
size_t frame_len)
{
struct l_genl_msg *cmd_associate, *cmd_deauth;
struct l_genl_attr attr;
uint16_t type, len;
const void *data;
uint16_t status_code;
const uint8_t *ies = NULL;
size_t ies_len;
const uint8_t *frame = NULL;
size_t frame_len = 0;
struct ie_tlv_iter iter;
const uint8_t *rsne = NULL;
const uint8_t *mde = NULL;
const uint8_t *fte = NULL;
struct handshake_state *hs = netdev->handshake;
bool is_rsn;
l_debug("");
if (!netdev->connected) {
l_warn("Unexpected connection related event -- "
"is another supplicant running?");
return;
}
/*
* During Fast Transition we use the authenticate event to start the
* reassociation step because the FTE necessary before we can build
* the FT Associate command is included in the attached frame and is
* not available in the Authenticate command callback.
*/
if (!netdev->in_ft)
return;
if (!l_genl_attr_init(&attr, msg)) {
l_debug("attr init failed");
goto auth_error;
}
while (l_genl_attr_next(&attr, &type, &len, &data)) {
switch (type) {
case NL80211_ATTR_TIMED_OUT:
l_warn("authentication timed out");
goto auth_error;
case NL80211_ATTR_FRAME:
if (frame)
goto auth_error;
frame = data;
frame_len = len;
break;
}
}
if (!frame)
goto auth_error;
/*
* Parse the Authentication Response and validate the contents
* according to 12.5.2 / 12.5.4: RSN or non-RSN Over-the-air
@ -2200,6 +2158,85 @@ ft_error:
netdev, NULL);
}
static void netdev_sae_process(struct netdev *netdev, const uint8_t *from,
const uint8_t *frame, size_t frame_len)
{
sae_rx_packet(netdev->sae_sm, from, frame, frame_len);
}
static void netdev_authenticate_event(struct l_genl_msg *msg,
struct netdev *netdev)
{
struct l_genl_attr attr;
uint16_t type, len;
const void *data;
const uint8_t *frame = NULL;
size_t frame_len = 0;
l_debug("");
if (!netdev->connected) {
l_warn("Unexpected connection related event -- "
"is another supplicant running?");
return;
}
/*
* During Fast Transition we use the authenticate event to start the
* reassociation step because the FTE necessary before we can build
* the FT Associate command is included in the attached frame and is
* not available in the Authenticate command callback.
*/
if (!netdev->in_ft && !netdev->sae_sm)
return;
if (!l_genl_attr_init(&attr, msg)) {
l_debug("attr init failed");
goto auth_error;
}
while (l_genl_attr_next(&attr, &type, &len, &data)) {
switch (type) {
case NL80211_ATTR_TIMED_OUT:
l_warn("authentication timed out");
if (netdev->sae_sm) {
sae_timeout(netdev->sae_sm);
return;
}
goto auth_error;
case NL80211_ATTR_FRAME:
if (frame)
goto auth_error;
frame = data;
frame_len = len;
break;
}
}
if (!frame)
goto auth_error;
if (netdev->in_ft)
netdev_ft_process(netdev, frame, frame_len);
else if (netdev->sae_sm)
netdev_sae_process(netdev,
((struct mmpdu_header *)frame)->address_2,
frame + 26, frame_len - 26);
else
goto auth_error;
return;
auth_error:
netdev->result = NETDEV_RESULT_AUTHENTICATION_FAILED;
netdev_connect_failed(NULL, netdev);
}
static void netdev_associate_event(struct l_genl_msg *msg,
struct netdev *netdev)
{
@ -2219,6 +2256,12 @@ static void netdev_cmd_connect_cb(struct l_genl_msg *msg, void *user_data)
NETDEV_EVENT_ASSOCIATING,
netdev->user_data);
/* the SAE SM can be freed */
if (netdev->sae_sm) {
sae_sm_free(netdev->sae_sm);
netdev->sae_sm = NULL;
}
/*
* We register the eapol state machine here, in case the PAE
* socket receives EAPoL packets before the nl80211 socket
@ -2236,6 +2279,81 @@ static void netdev_cmd_connect_cb(struct l_genl_msg *msg, void *user_data)
netdev_connect_failed(NULL, netdev);
}
static void netdev_sae_complete(uint16_t status, void *user_data)
{
struct netdev *netdev = user_data;
struct l_genl_msg *msg;
if (status != 0) {
l_error("SAE exchange failed on %u result %u",
netdev->index, status);
netdev->sae_sm = NULL;
goto auth_failed;
}
msg = netdev_build_cmd_associate_common(netdev);
l_genl_msg_append_attr(msg, NL80211_ATTR_IE,
netdev->handshake->own_ie[1] + 2,
netdev->handshake->own_ie);
/* netdev_cmd_connect_cb can be reused */
netdev->connect_cmd_id = l_genl_family_send(nl80211, msg,
netdev_cmd_connect_cb,
netdev, NULL);
if (!netdev->connect_cmd_id)
goto auth_failed;
/*
* Kick off EAPoL sm early in case the first EAPoL packet comes prior
* to the netdev_cmd_connect_cb
*/
netdev->sm = eapol_sm_new(netdev->handshake);
return;
auth_failed:
netdev->result = NETDEV_RESULT_AUTHENTICATION_FAILED;
netdev_connect_failed(NULL, netdev);
}
static void netdev_tx_sae_frame_cb(struct l_genl_msg *msg,
void *user_data)
{
int err = l_genl_msg_get_error(msg);
if (err < 0)
l_debug("SAE: CMD_AUTHENTICATE failed: %s", strerror(err));
}
static int netdev_tx_sae_frame(const uint8_t *dest, const uint8_t *body,
size_t body_len, void *user_data)
{
struct netdev *netdev = user_data;
struct l_genl_msg *msg;
uint32_t auth_type = NL80211_AUTHTYPE_SAE;
msg = l_genl_msg_new_sized(NL80211_CMD_AUTHENTICATE, 512);
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ,
4, &netdev->frequency);
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, dest);
l_genl_msg_append_attr(msg, NL80211_ATTR_SSID,
strlen((char *) netdev->handshake->ssid),
netdev->handshake->ssid);
l_genl_msg_append_attr(msg, NL80211_ATTR_AUTH_TYPE, 4, &auth_type);
l_genl_msg_append_attr(msg, NL80211_ATTR_AUTH_DATA, body_len, body);
if (!l_genl_family_send(nl80211, msg, netdev_tx_sae_frame_cb,
netdev, NULL)) {
l_genl_msg_unref(msg);
return -EINVAL;
}
return 0;
}
static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev,
struct scan_bss *bss,
struct handshake_state *hs,
@ -2337,13 +2455,15 @@ static int netdev_connect_common(struct netdev *netdev,
netdev_event_func_t event_filter,
netdev_connect_cb_t cb, void *user_data)
{
netdev->connect_cmd_id = l_genl_family_send(nl80211, cmd_connect,
netdev_cmd_connect_cb,
netdev, NULL);
if (cmd_connect) {
netdev->connect_cmd_id = l_genl_family_send(nl80211,
cmd_connect, netdev_cmd_connect_cb,
netdev, NULL);
if (!netdev->connect_cmd_id) {
l_genl_msg_unref(cmd_connect);
return -EIO;
if (!netdev->connect_cmd_id) {
l_genl_msg_unref(cmd_connect);
return -EIO;
}
}
netdev->event_filter = event_filter;
@ -2360,6 +2480,9 @@ 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 (netdev->sae_sm)
sae_start(netdev->sae_sm);
return 0;
}
@ -2368,7 +2491,7 @@ int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
netdev_event_func_t event_filter,
netdev_connect_cb_t cb, void *user_data)
{
struct l_genl_msg *cmd_connect;
struct l_genl_msg *cmd_connect = NULL;
struct eapol_sm *sm = NULL;
bool is_rsn = hs->own_ie != NULL;
@ -2378,12 +2501,18 @@ int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
if (netdev->connected)
return -EISCONN;
cmd_connect = netdev_build_cmd_connect(netdev, bss, hs, NULL);
if (!cmd_connect)
return -EINVAL;
if (hs->akm_suite == IE_RSN_AKM_SUITE_SAE_SHA256) {
netdev->sae_sm = sae_sm_new(hs, netdev_tx_sae_frame,
netdev_sae_complete, netdev);
} else {
cmd_connect = netdev_build_cmd_connect(netdev, bss, hs, NULL);
if (is_rsn)
sm = eapol_sm_new(hs);
if (!cmd_connect)
return -EINVAL;
if (is_rsn)
sm = eapol_sm_new(hs);
}
return netdev_connect_common(netdev, cmd_connect, bss, hs, sm,
event_filter, cb, user_data);