netdev: add FILS support

From netdev's prospective FILS works the same as OWE/SAE where we create
a fils_sm and forward all auth/assoc frames into the FILS module. The
only real difference is we do not start EAPoL once FILS completes.
This commit is contained in:
James Prestwood 2019-04-22 10:11:45 -07:00 committed by Denis Kenzior
parent 73c9a126bd
commit b125976fea
1 changed files with 104 additions and 6 deletions

View File

@ -59,6 +59,7 @@
#include "src/sae.h"
#include "src/nl80211util.h"
#include "src/owe.h"
#include "src/fils.h"
#ifndef ENOTSUPP
#define ENOTSUPP 524
@ -96,6 +97,7 @@ struct netdev {
struct eapol_sm *sm;
struct sae_sm *sae_sm;
struct owe_sm *owe;
struct fils_sm *fils;
struct handshake_state *handshake;
uint32_t connect_cmd_id;
uint32_t disconnect_cmd_id;
@ -2366,7 +2368,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 && !netdev->owe)
if (!netdev->in_ft && !netdev->sae_sm && !netdev->owe && !netdev->fils)
return;
if (!l_genl_attr_init(&attr, msg)) {
@ -2408,6 +2410,8 @@ static void netdev_authenticate_event(struct l_genl_msg *msg,
frame + 26, frame_len - 26);
else if (netdev->owe)
owe_rx_authenticate(netdev->owe);
else if (netdev->fils)
fils_rx_authenticate(netdev->fils, frame, frame_len);
else
goto auth_error;
@ -2433,7 +2437,8 @@ static void netdev_associate_event(struct l_genl_msg *msg,
if (netdev->aborting)
return;
if (!netdev->owe && !netdev->in_ft && !netdev->handshake->mde)
if (!netdev->owe && !netdev->in_ft && !netdev->handshake->mde &&
!netdev->fils)
return;
if (!l_genl_attr_init(&attr, msg)) {
@ -2461,6 +2466,9 @@ static void netdev_associate_event(struct l_genl_msg *msg,
if (netdev->owe) {
owe_rx_associate(netdev->owe, frame, frame_len);
return;
} else if (netdev->fils) {
fils_rx_associate(netdev->fils, frame, frame_len);
return;
}
if (!netdev_ft_process_associate(netdev, frame, frame_len,
@ -2638,7 +2646,7 @@ 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)
static void netdev_auth_cb(struct l_genl_msg *msg, void *user_data)
{
struct netdev *netdev = user_data;
@ -2661,7 +2669,7 @@ static void netdev_owe_tx_authenticate(void *user_data)
NL80211_AUTHTYPE_OPEN_SYSTEM,
netdev->handshake->aa);
if (!l_genl_family_send(nl80211, msg, netdev_owe_auth_cb,
if (!l_genl_family_send(nl80211, msg, netdev_auth_cb,
netdev, NULL)) {
l_genl_msg_unref(msg);
netdev_connect_failed(netdev,
@ -2670,7 +2678,7 @@ static void netdev_owe_tx_authenticate(void *user_data)
}
}
static void netdev_owe_assoc_cb(struct l_genl_msg *msg, void *user_data)
static void netdev_assoc_cb(struct l_genl_msg *msg, void *user_data)
{
struct netdev *netdev = user_data;
@ -2692,7 +2700,7 @@ static void netdev_owe_tx_associate(struct iovec *ie_iov, size_t iov_len,
l_genl_msg_append_attrv(msg, NL80211_ATTR_IE, ie_iov, iov_len);
if (!l_genl_family_send(nl80211, msg, netdev_owe_assoc_cb,
if (!l_genl_family_send(nl80211, msg, netdev_assoc_cb,
netdev, NULL)) {
l_genl_msg_unref(msg);
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
@ -2727,6 +2735,88 @@ static void netdev_owe_complete(uint16_t status, void *user_data)
eapol_start(netdev->sm);
}
static void netdev_fils_tx_authenticate(const uint8_t *body,
size_t body_len,
void *user_data)
{
struct netdev *netdev = user_data;
struct l_genl_msg *msg;
msg = netdev_build_cmd_authenticate(netdev, NL80211_AUTHTYPE_FILS_SK,
netdev->handshake->aa);
l_genl_msg_append_attr(msg, NL80211_ATTR_AUTH_DATA, body_len, body);
if (!l_genl_family_send(nl80211, msg, netdev_auth_cb,
netdev, NULL)) {
l_genl_msg_unref(msg);
netdev_connect_failed(netdev,
NETDEV_RESULT_AUTHENTICATION_FAILED,
MMPDU_STATUS_CODE_UNSPECIFIED);
}
}
static void netdev_fils_tx_associate(struct iovec *iov, size_t iov_len,
const uint8_t *kek, size_t kek_len,
const uint8_t *nonces, size_t nonces_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, iov, iov_len);
l_genl_msg_append_attr(msg, NL80211_ATTR_FILS_KEK, kek_len, kek);
l_genl_msg_append_attr(msg, NL80211_ATTR_FILS_NONCES, nonces_len, nonces);
if (!l_genl_family_send(nl80211, msg, netdev_assoc_cb,
netdev, NULL)) {
l_genl_msg_unref(msg);
netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED,
MMPDU_STATUS_CODE_UNSPECIFIED);
}
}
static void netdev_fils_complete(uint16_t status, bool in_auth, bool ap_reject,
void *user_data)
{
struct netdev *netdev = user_data;
fils_sm_free(netdev->fils);
netdev->fils = NULL;
if (status == 0) {
netdev->ignore_connect_event = true;
/* Register SM for rekeying */
netdev->sm = eapol_sm_new(netdev->handshake);
eapol_register(netdev->sm);
eapol_set_started(netdev->sm);
return;
}
/*
* There are a few scenarios here:
*
* 1. AP rejected authentication, this case we are done.
* 2. AP accepted either authentication or association where status was
* zero, but we failed for some other reason. In these cases we
* should set expect_connect_failure which causes a deauth.
* 3. AP rejected association. This will be a non zero status code, so
* the kernel should know that we have failed the connection.
*/
if (!ap_reject)
netdev->expect_connect_failure = true;
netdev->result = (in_auth) ? NETDEV_RESULT_AUTHENTICATION_FAILED :
NETDEV_RESULT_ASSOCIATION_FAILED;
netdev->last_code = status;
}
static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev,
struct scan_bss *bss,
struct handshake_state *hs,
@ -2861,6 +2951,8 @@ static int netdev_connect_common(struct netdev *netdev,
sae_start(netdev->sae_sm);
else if (netdev->owe)
owe_start(netdev->owe);
else if (netdev->fils)
fils_start(netdev->fils);
return 0;
}
@ -2892,6 +2984,12 @@ int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
netdev_owe_complete,
netdev);
break;
case IE_RSN_AKM_SUITE_FILS_SHA256:
case IE_RSN_AKM_SUITE_FILS_SHA384:
netdev->fils = fils_sm_new(hs, netdev_fils_tx_authenticate,
netdev_fils_tx_associate,
netdev_fils_complete, netdev);
break;
default:
cmd_connect = netdev_build_cmd_connect(netdev, bss, hs, NULL);