From b125976fea833bba562d773e8128bcb692b26253 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Mon, 22 Apr 2019 10:11:45 -0700 Subject: [PATCH] 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. --- src/netdev.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 6 deletions(-) diff --git a/src/netdev.c b/src/netdev.c index 39d748e0..18d37949 100644 --- a/src/netdev.c +++ b/src/netdev.c @@ -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);