netdev/station: Add OWE support

The changes to station.c are minor. Specifically,
station_build_handshake_rsn was modified to always build up the RSN
information, not just for SECURITY_8021X and SECURITY_PSK. This is
because OWE needs this RSN information, even though it is still
SECURITY_NONE. Since "regular" open networks don't need this, a check
was added (security == NONE && akm != OWE) which skips the RSN
building.

netdev.c needed to be changed in nearly the same manor as it was for
SAE. When connecting, we check if the AKM is for OWE, and if so create
a new OWE SM and start it. OWE handles all the ECDH, and netdev handles
sending CMD_AUTHENTICATE and CMD_ASSOCIATE when triggered by OWE. The
incoming authenticate/associate events just get forwarded to OWE as they
do with SAE.
This commit is contained in:
James Prestwood 2018-11-16 14:22:55 -08:00 committed by Denis Kenzior
parent 8978f8c43f
commit 576c6dc9f3
2 changed files with 245 additions and 84 deletions

View File

@ -58,6 +58,7 @@
#include "src/watchlist.h"
#include "src/sae.h"
#include "src/nl80211util.h"
#include "src/owe.h"
#ifndef ENOTSUPP
#define ENOTSUPP 524
@ -94,6 +95,7 @@ struct netdev {
void *user_data;
struct eapol_sm *sm;
struct sae_sm *sae_sm;
struct owe_sm *owe;
struct handshake_state *handshake;
uint32_t connect_cmd_id;
uint32_t disconnect_cmd_id;
@ -543,6 +545,11 @@ static void netdev_connect_free(struct netdev *netdev)
netdev->sae_sm = NULL;
}
if (netdev->owe) {
owe_sm_free(netdev->owe);
netdev->owe = NULL;
}
eapol_preauth_cancel(netdev->index);
if (netdev->handshake) {
@ -1786,8 +1793,10 @@ static void netdev_connect_event(struct l_genl_msg *msg,
}
}
if (!netdev_handle_associate_resp_ies(netdev->handshake, rsne, mde, fte,
netdev->in_ft))
/* OWE can skip this since it handles associate itself */
if (!netdev->owe && !netdev_handle_associate_resp_ies(netdev->handshake,
rsne, mde, fte,
netdev->in_ft))
goto error;
if (netdev->sm) {
@ -1813,6 +1822,10 @@ static void netdev_connect_event(struct l_genl_msg *msg,
}
}
/* OWE must have failed, this is handled inside netdev_owe_complete */
if (netdev->owe && !netdev->sm)
return;
netdev_connect_ok(netdev);
return;
@ -2268,7 +2281,7 @@ static void netdev_authenticate_event(struct l_genl_msg *msg,
* 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)
if (!netdev->in_ft && !netdev->sae_sm && !netdev->owe)
return;
if (!l_genl_attr_init(&attr, msg)) {
@ -2308,6 +2321,8 @@ static void netdev_authenticate_event(struct l_genl_msg *msg,
netdev_sae_process(netdev,
((struct mmpdu_header *)frame)->address_2,
frame + 26, frame_len - 26);
else if (netdev->owe)
owe_rx_authenticate(netdev->owe);
else
goto auth_error;
@ -2321,7 +2336,40 @@ auth_error:
static void netdev_associate_event(struct l_genl_msg *msg,
struct netdev *netdev)
{
struct l_genl_attr attr;
uint16_t type, len;
const void *data;
size_t frame_len = 0;
const uint8_t *frame;
l_debug("");
if (!netdev->owe)
return;
if (!l_genl_attr_init(&attr, msg)) {
l_debug("attr init failed");
return;
}
while (l_genl_attr_next(&attr, &type, &len, &data)) {
switch (type) {
case NL80211_ATTR_TIMED_OUT:
l_warn("authentication timed out");
goto assoc_failed;
case NL80211_ATTR_FRAME:
frame = data;
frame_len = len;
owe_rx_associate(netdev->owe, frame, frame_len);
return;
}
}
assoc_failed:
netdev->result = NETDEV_RESULT_ASSOCIATION_FAILED;
netdev_connect_failed(NULL, netdev);
}
static void netdev_cmd_connect_cb(struct l_genl_msg *msg, void *user_data)
@ -2360,6 +2408,28 @@ static void netdev_cmd_connect_cb(struct l_genl_msg *msg, void *user_data)
netdev_connect_failed(NULL, netdev);
}
static struct l_genl_msg *netdev_build_cmd_authenticate(
struct netdev *netdev,
uint32_t auth_type,
const uint8_t *addr)
{
struct handshake_state *hs = netdev->handshake;
struct l_genl_msg *msg;
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, addr);
l_genl_msg_append_attr(msg, NL80211_ATTR_SSID, hs->ssid_len, hs->ssid);
l_genl_msg_append_attr(msg, NL80211_ATTR_AUTH_TYPE, 4, &auth_type);
l_genl_msg_append_attr(msg, NL80211_ATTR_IE, hs->supplicant_ie[1] + 2,
hs->supplicant_ie);
return msg;
}
static void netdev_sae_complete(uint16_t status, void *user_data)
{
struct netdev *netdev = user_data;
@ -2422,17 +2492,8 @@ static int netdev_tx_sae_frame(const uint8_t *dest, const uint8_t *body,
{
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);
msg = netdev_build_cmd_authenticate(netdev, NL80211_AUTHTYPE_SAE, dest);
l_genl_msg_append_attr(msg, NL80211_ATTR_AUTH_DATA, body_len, body);
@ -2445,6 +2506,87 @@ static int netdev_tx_sae_frame(const uint8_t *dest, const uint8_t *body,
return 0;
}
static void netdev_owe_auth_cb(struct l_genl_msg *msg, void *user_data)
{
struct netdev *netdev = user_data;
if (l_genl_msg_get_error(msg) < 0) {
l_error("Error sending CMD_AUTHENTICATE");
netdev->result = NETDEV_RESULT_AUTHENTICATION_FAILED;
netdev_connect_failed(NULL, netdev);
return;
}
}
static void netdev_owe_tx_authenticate(void *user_data)
{
struct netdev *netdev = user_data;
struct l_genl_msg *msg;
msg = netdev_build_cmd_authenticate(netdev,
NL80211_AUTHTYPE_OPEN_SYSTEM,
netdev->handshake->aa);
if (!l_genl_family_send(nl80211, msg, netdev_owe_auth_cb, netdev, NULL)) {
l_genl_msg_unref(msg);
netdev->result = NETDEV_RESULT_AUTHENTICATION_FAILED;
netdev_connect_failed(NULL, netdev);
}
}
static void netdev_owe_assoc_cb(struct l_genl_msg *msg, void *user_data)
{
struct netdev *netdev = user_data;
if (l_genl_msg_get_error(msg) < 0) {
l_error("Error sending CMD_ASSOCIATE");
netdev->result = NETDEV_RESULT_ASSOCIATION_FAILED;
netdev_connect_failed(NULL, netdev);
}
}
static void netdev_owe_tx_associate(struct iovec *ie_iov, size_t iov_len,
void *user_data)
{
struct netdev *netdev = user_data;
struct l_genl_msg *msg;
msg = netdev_build_cmd_associate_common(netdev);
l_genl_msg_append_attrv(msg, NL80211_ATTR_IE, ie_iov, iov_len);
if (!l_genl_family_send(nl80211, msg, netdev_owe_assoc_cb,
netdev, NULL)) {
l_genl_msg_unref(msg);
netdev->result = NETDEV_RESULT_ASSOCIATION_FAILED;
netdev_connect_failed(NULL, netdev);
}
}
static void netdev_owe_complete(uint16_t status, void *user_data)
{
struct netdev *netdev = user_data;
struct l_genl_msg *msg;
if (status) {
/*
* OWE will never fail during authenticate, at least internally,
* so we can always assume its association that failed.
*/
netdev->result = NETDEV_RESULT_ASSOCIATION_FAILED;
msg = netdev_build_cmd_disconnect(netdev, status);
netdev->disconnect_cmd_id = l_genl_family_send(nl80211, msg,
netdev_connect_failed,
netdev, NULL);
return;
}
netdev->sm = eapol_sm_new(netdev->handshake);
eapol_register(netdev->sm);
}
static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev,
struct scan_bss *bss,
struct handshake_state *hs,
@ -2573,6 +2715,8 @@ static int netdev_connect_common(struct netdev *netdev,
if (netdev->sae_sm)
sae_start(netdev->sae_sm);
else if (netdev->owe)
owe_start(netdev->owe);
return 0;
}
@ -2592,10 +2736,19 @@ int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
if (netdev->connected)
return -EISCONN;
if (IE_AKM_IS_SAE(hs->akm_suite)) {
switch (hs->akm_suite) {
case IE_RSN_AKM_SUITE_SAE_SHA256:
case IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256:
netdev->sae_sm = sae_sm_new(hs, netdev_tx_sae_frame,
netdev_sae_complete, netdev);
} else {
break;
case IE_RSN_AKM_SUITE_OWE:
netdev->owe = owe_sm_new(hs, netdev_owe_tx_authenticate,
netdev_owe_tx_associate,
netdev_owe_complete,
netdev);
break;
default:
cmd_connect = netdev_build_cmd_connect(netdev, bss, hs, NULL);
if (!cmd_connect)

View File

@ -416,86 +416,94 @@ static int station_build_handshake_rsn(struct handshake_state *hs,
enum security security = network_get_security(network);
bool add_mde = false;
if (security == SECURITY_PSK || security == SECURITY_8021X) {
const struct l_settings *settings = iwd_get_config();
struct ie_rsn_info bss_info;
uint8_t rsne_buf[256];
struct ie_rsn_info info;
uint32_t mfp_setting;
const struct l_settings *settings = iwd_get_config();
struct ie_rsn_info bss_info;
uint8_t rsne_buf[256];
struct ie_rsn_info info;
uint32_t mfp_setting;
memset(&info, 0, sizeof(info));
memset(&info, 0, sizeof(info));
memset(&bss_info, 0, sizeof(bss_info));
scan_bss_get_rsn_info(bss, &bss_info);
memset(&bss_info, 0, sizeof(bss_info));
scan_bss_get_rsn_info(bss, &bss_info);
info.akm_suites = wiphy_select_akm(wiphy, bss);
info.akm_suites = wiphy_select_akm(wiphy, bss);
if (!info.akm_suites)
goto not_supported;
/*
* Special case for OWE. With OWE we still need to build up the
* handshake object with AKM/cipher info since OWE does the full 4-way
* handshake. But if this is a non-OWE open network, we can skip this.
*/
if (security == SECURITY_NONE &&
!(info.akm_suites & IE_RSN_AKM_SUITE_OWE))
goto open_network;
info.pairwise_ciphers = wiphy_select_cipher(wiphy,
bss_info.pairwise_ciphers);
info.group_cipher = wiphy_select_cipher(wiphy,
bss_info.group_cipher);
if (!info.akm_suites)
goto not_supported;
if (!info.pairwise_ciphers || !info.group_cipher)
goto not_supported;
info.pairwise_ciphers = wiphy_select_cipher(wiphy,
bss_info.pairwise_ciphers);
info.group_cipher = wiphy_select_cipher(wiphy,
bss_info.group_cipher);
if (!l_settings_get_uint(settings, "General",
"ManagementFrameProtection", &mfp_setting))
mfp_setting = 1;
if (!info.pairwise_ciphers || !info.group_cipher)
goto not_supported;
if (mfp_setting > 2) {
l_error("Invalid MFP value, using default of 1");
mfp_setting = 1;
}
if (!l_settings_get_uint(settings, "General",
"ManagementFrameProtection", &mfp_setting))
mfp_setting = 1;
switch (mfp_setting) {
case 0:
break;
case 1:
info.group_management_cipher =
wiphy_select_cipher(wiphy,
bss_info.group_management_cipher);
info.mfpc = info.group_management_cipher != 0;
break;
case 2:
info.group_management_cipher =
wiphy_select_cipher(wiphy,
bss_info.group_management_cipher);
/*
* MFP required on our side, but AP doesn't support MFP
* or cipher mismatch
*/
if (info.group_management_cipher == 0)
goto not_supported;
info.mfpc = true;
info.mfpr = true;
break;
}
if (bss_info.mfpr && !info.mfpc)
goto not_supported;
/* RSN takes priority */
if (bss->rsne) {
ie_build_rsne(&info, rsne_buf);
handshake_state_set_authenticator_rsn(hs, bss->rsne);
handshake_state_set_supplicant_rsn(hs, rsne_buf);
} else {
ie_build_wpa(&info, rsne_buf);
handshake_state_set_authenticator_wpa(hs, bss->wpa);
handshake_state_set_supplicant_wpa(hs, rsne_buf);
}
if (info.akm_suites & (IE_RSN_AKM_SUITE_FT_OVER_8021X |
IE_RSN_AKM_SUITE_FT_USING_PSK |
IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256))
add_mde = true;
if (mfp_setting > 2) {
l_error("Invalid MFP value, using default of 1");
mfp_setting = 1;
}
switch (mfp_setting) {
case 0:
break;
case 1:
info.group_management_cipher =
wiphy_select_cipher(wiphy,
bss_info.group_management_cipher);
info.mfpc = info.group_management_cipher != 0;
break;
case 2:
info.group_management_cipher =
wiphy_select_cipher(wiphy,
bss_info.group_management_cipher);
/*
* MFP required on our side, but AP doesn't support MFP
* or cipher mismatch
*/
if (info.group_management_cipher == 0)
goto not_supported;
info.mfpc = true;
info.mfpr = true;
break;
}
if (bss_info.mfpr && !info.mfpc)
goto not_supported;
/* RSN takes priority */
if (bss->rsne) {
ie_build_rsne(&info, rsne_buf);
handshake_state_set_authenticator_rsn(hs, bss->rsne);
handshake_state_set_supplicant_rsn(hs, rsne_buf);
} else {
ie_build_wpa(&info, rsne_buf);
handshake_state_set_authenticator_wpa(hs, bss->wpa);
handshake_state_set_supplicant_wpa(hs, rsne_buf);
}
if (info.akm_suites & (IE_RSN_AKM_SUITE_FT_OVER_8021X |
IE_RSN_AKM_SUITE_FT_USING_PSK |
IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256))
add_mde = true;
open_network:
if (security == SECURITY_NONE)
/* Perform FT association if available */
add_mde = bss->mde_present;