fils: netdev: update to use auth_proto concepts

This commit is contained in:
James Prestwood 2019-05-03 11:59:51 -07:00 committed by Denis Kenzior
parent f0de2516ea
commit 8317b96e7d
3 changed files with 111 additions and 132 deletions

View File

@ -34,18 +34,19 @@
#include "src/util.h"
#include "src/missing.h"
#include "src/erp.h"
#include "src/auth-proto.h"
#define FILS_NONCE_LEN 16
#define FILS_SESSION_LEN 8
struct fils_sm {
struct auth_proto ap;
struct erp_state *erp;
struct handshake_state *hs;
void *user_data;
fils_tx_authenticate_func_t auth;
fils_tx_associate_func_t assoc;
fils_complete_func_t complete;
uint8_t nonce[FILS_NONCE_LEN];
uint8_t anonce[FILS_NONCE_LEN];
@ -58,15 +59,8 @@ struct fils_sm {
uint8_t pmk[48];
size_t pmk_len;
uint8_t pmkid[16];
bool in_auth : 1;
};
static void fils_failed(struct fils_sm *fils, uint16_t status, bool ap_reject)
{
fils->complete(status, fils->in_auth, ap_reject, fils->user_data);
}
static void fils_derive_pmkid(struct fils_sm *fils, const uint8_t *erp_data,
size_t len)
{
@ -237,34 +231,20 @@ static int fils_derive_key_data(struct fils_sm *fils)
fils->assoc(iov, 2, fils->kek_and_tk, fils->kek_len, data,
FILS_NONCE_LEN * 2, fils->user_data);
fils->in_auth = false;
return 0;
}
struct fils_sm *fils_sm_new(struct handshake_state *hs,
fils_tx_authenticate_func_t auth,
fils_tx_associate_func_t assoc,
fils_complete_func_t complete, void *user_data)
static bool fils_start(struct auth_proto *driver)
{
struct fils_sm *fils;
struct fils_sm *fils = l_container_of(driver, struct fils_sm, ap);
fils = l_new(struct fils_sm, 1);
fils->auth = auth;
fils->assoc = assoc;
fils->complete = complete;
fils->user_data = user_data;
fils->hs = hs;
fils->in_auth = true;
fils->erp = erp_new(hs->erp_cache, fils_erp_tx_func, fils);
return fils;
return erp_start(fils->erp);
}
void fils_sm_free(struct fils_sm *fils)
static void fils_free(struct auth_proto *driver)
{
struct fils_sm *fils = l_container_of(driver, struct fils_sm, ap);
erp_free(fils->erp);
explicit_bzero(fils->ick, sizeof(fils->ick));
@ -275,16 +255,10 @@ void fils_sm_free(struct fils_sm *fils)
l_free(fils);
}
void fils_start(struct fils_sm *fils)
{
if (!erp_start(fils->erp))
fils->complete(MMPDU_STATUS_CODE_UNSPECIFIED, fils->in_auth,
false, fils->user_data);
}
void fils_rx_authenticate(struct fils_sm *fils, const uint8_t *frame,
static int fils_rx_authenticate(struct auth_proto *driver, const uint8_t *frame,
size_t len)
{
struct fils_sm *fils = l_container_of(driver, struct fils_sm, ap);
const struct mmpdu_header *hdr = mpdu_validate(frame, len);
const struct mmpdu_authentication *auth;
struct ie_tlv_iter iter;
@ -295,27 +269,25 @@ void fils_rx_authenticate(struct fils_sm *fils, const uint8_t *frame,
if (!hdr) {
l_debug("Auth frame header did not validate");
goto auth_failed;
return -EBADMSG;
}
auth = mmpdu_body(hdr);
if (!auth) {
l_debug("Auth frame body did not validate");
goto auth_failed;
return -EBADMSG;
}
if (auth->status != 0) {
l_debug("invalid status %u", auth->status);
fils_failed(fils, auth->status, true);
return;
return (int)auth->status;
}
if (auth->algorithm != MMPDU_AUTH_ALGO_FILS_SK &&
auth->algorithm != MMPDU_AUTH_ALGO_FILS_SK_PFS) {
l_debug("invalid auth algorithm %u", auth->algorithm);
fils_failed(fils, MMPDU_STATUS_CODE_UNSUP_AUTH_ALG, false);
return;
return MMPDU_STATUS_CODE_UNSUP_AUTH_ALG;
}
ie_tlv_iter_init(&iter, auth->ies, (const uint8_t *) hdr + len -
@ -324,13 +296,13 @@ void fils_rx_authenticate(struct fils_sm *fils, const uint8_t *frame,
switch (iter.tag) {
case IE_TYPE_FILS_NONCE:
if (iter.len != FILS_NONCE_LEN)
goto auth_failed;
goto invalid_ies;
anonce = iter.data;
break;
case IE_TYPE_FILS_SESSION:
if (iter.len != FILS_SESSION_LEN)
goto auth_failed;
goto invalid_ies;
session = iter.data;
break;
@ -345,24 +317,24 @@ void fils_rx_authenticate(struct fils_sm *fils, const uint8_t *frame,
if (!anonce || !session || !wrapped) {
l_debug("Auth did not include required IEs");
fils_failed(fils, MMPDU_STATUS_CODE_INVALID_ELEMENT, false);
return;
goto invalid_ies;
}
memcpy(fils->anonce, anonce, FILS_NONCE_LEN);
if (erp_rx_packet(fils->erp, wrapped, wrapped_len) < 0)
goto auth_failed;
goto invalid_ies;
fils_derive_key_data(fils);
return;
return fils_derive_key_data(fils);
auth_failed:
fils_failed(fils, MMPDU_REASON_CODE_UNSPECIFIED, false);
invalid_ies:
return MMPDU_STATUS_CODE_INVALID_ELEMENT;
}
void fils_rx_associate(struct fils_sm *fils, const uint8_t *frame, size_t len)
static int fils_rx_associate(struct auth_proto *driver, const uint8_t *frame,
size_t len)
{
struct fils_sm *fils = l_container_of(driver, struct fils_sm, ap);
const struct mmpdu_header *hdr = mpdu_validate(frame, len);
const struct mmpdu_association_response *assoc;
struct ie_tlv_iter iter;
@ -381,20 +353,18 @@ void fils_rx_associate(struct fils_sm *fils, const uint8_t *frame, size_t len)
if (!hdr) {
l_debug("Assoc frame header did not validate");
goto assoc_failed;
return -EBADMSG;
}
assoc = mmpdu_body(hdr);
if (!assoc) {
l_debug("Assoc frame body did not validate");
goto assoc_failed;;
return -EBADMSG;
}
if (assoc->status_code != 0) {
fils_failed(fils, assoc->status_code, true);
return;
}
if (assoc->status_code != 0)
return (int)assoc->status_code;
ie_tlv_iter_init(&iter, assoc->ies, (const uint8_t *) hdr + len -
assoc->ies);
@ -467,7 +437,7 @@ void fils_rx_associate(struct fils_sm *fils, const uint8_t *frame, size_t len)
if (memcmp(ap_key_auth, expected_key_auth, fils->ick_len)) {
l_error("AP KeyAuth did not verify");
goto assoc_failed;
return -EBADMSG;
}
handshake_state_set_pmk(fils->hs, fils->pmk, fils->pmk_len);
@ -484,14 +454,32 @@ void fils_rx_associate(struct fils_sm *fils, const uint8_t *frame, size_t len)
handshake_state_set_ptk(fils->hs, fils->kek_and_tk, fils->kek_len + 16);
handshake_state_install_ptk(fils->hs);
fils->complete(0, fils->in_auth, false, fils->user_data);
return;
assoc_failed:
fils_failed(fils, MMPDU_STATUS_CODE_UNSPECIFIED, false);
return;
return 0;
invalid_ies:
fils_failed(fils, MMPDU_STATUS_CODE_INVALID_ELEMENT, false);
return MMPDU_STATUS_CODE_INVALID_ELEMENT;
}
struct auth_proto *fils_sm_new(struct handshake_state *hs,
fils_tx_authenticate_func_t auth,
fils_tx_associate_func_t assoc,
void *user_data)
{
struct fils_sm *fils;
fils = l_new(struct fils_sm, 1);
fils->auth = auth;
fils->assoc = assoc;
fils->user_data = user_data;
fils->hs = hs;
fils->ap.start = fils_start;
fils->ap.free = fils_free;
fils->ap.rx_authenticate = fils_rx_authenticate;
fils->ap.rx_associate = fils_rx_associate;
fils->erp = erp_new(hs->erp_cache, fils_erp_tx_func, fils);
return &fils->ap;
}

View File

@ -30,18 +30,8 @@ typedef void (*fils_tx_associate_func_t)(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);
typedef void (*fils_complete_func_t)(uint16_t status, bool in_auth,
bool ap_reject, void *user_data);
struct fils_sm *fils_sm_new(struct handshake_state *hs,
struct auth_proto *fils_sm_new(struct handshake_state *hs,
fils_tx_authenticate_func_t auth,
fils_tx_associate_func_t assoc,
fils_complete_func_t complete, void *user_data);
void fils_sm_free(struct fils_sm *fils);
void fils_start(struct fils_sm *fils);
void fils_rx_authenticate(struct fils_sm *fils, const uint8_t *frame,
size_t len);
void fils_rx_associate(struct fils_sm *fils, const uint8_t *frame, size_t len);
void *user_data);

View File

@ -60,6 +60,7 @@
#include "src/nl80211util.h"
#include "src/owe.h"
#include "src/fils.h"
#include "src/auth-proto.h"
#ifndef ENOTSUPP
#define ENOTSUPP 524
@ -97,7 +98,7 @@ struct netdev {
struct eapol_sm *sm;
struct sae_sm *sae_sm;
struct owe_sm *owe;
struct fils_sm *fils;
struct auth_proto *ap;
struct handshake_state *handshake;
uint32_t connect_cmd_id;
uint32_t disconnect_cmd_id;
@ -561,6 +562,11 @@ static void netdev_connect_free(struct netdev *netdev)
netdev->owe = NULL;
}
if (netdev->ap) {
auth_proto_free(netdev->ap);
netdev->ap = NULL;
}
eapol_preauth_cancel(netdev->index);
if (netdev->handshake) {
@ -2350,6 +2356,8 @@ static void netdev_authenticate_event(struct l_genl_msg *msg,
const void *data;
const uint8_t *frame = NULL;
size_t frame_len = 0;
int ret;
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
l_debug("");
@ -2368,7 +2376,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 && !netdev->fils)
if (!netdev->in_ft && !netdev->sae_sm && !netdev->owe && !netdev->ap)
return;
if (!l_genl_attr_init(&attr, msg)) {
@ -2385,7 +2393,8 @@ static void netdev_authenticate_event(struct l_genl_msg *msg,
if (netdev->sae_sm) {
sae_timeout(netdev->sae_sm);
return;
}
} else if (auth_proto_auth_timeout(netdev->ap))
return;
goto auth_error;
@ -2410,16 +2419,22 @@ 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
else if (netdev->ap) {
ret = auth_proto_rx_authenticate(netdev->ap, frame, frame_len);
if (ret == 0 || ret == -EAGAIN)
return;
else if (ret > 0)
status_code = (uint16_t)ret;
goto auth_error;
} else
goto auth_error;
return;
auth_error:
netdev_connect_failed(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED,
MMPDU_STATUS_CODE_UNSPECIFIED);
status_code);
}
static void netdev_associate_event(struct l_genl_msg *msg,
@ -2431,6 +2446,7 @@ static void netdev_associate_event(struct l_genl_msg *msg,
size_t frame_len = 0;
const uint8_t *frame = NULL;
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
int ret;
l_debug("");
@ -2438,7 +2454,7 @@ static void netdev_associate_event(struct l_genl_msg *msg,
return;
if (!netdev->owe && !netdev->in_ft && !netdev->handshake->mde &&
!netdev->fils)
!netdev->ap)
return;
if (!l_genl_attr_init(&attr, msg)) {
@ -2450,6 +2466,10 @@ static void netdev_associate_event(struct l_genl_msg *msg,
switch (type) {
case NL80211_ATTR_TIMED_OUT:
l_warn("association timed out");
if (auth_proto_assoc_timeout(netdev->ap))
return;
goto assoc_failed;
case NL80211_ATTR_FRAME:
@ -2466,9 +2486,27 @@ 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;
} else if (netdev->ap) {
ret = auth_proto_rx_associate(netdev->ap, frame, frame_len);
if (ret == 0) {
auth_proto_free(netdev->ap);
netdev->ap = NULL;
netdev->sm = eapol_sm_new(netdev->handshake);
eapol_register(netdev->sm);
goto auth_complete;
} else if (ret == -EAGAIN) {
/*
* Here to support OWE retries. OWE will retry
* internally, but a connect even will still be emitted.
*/
netdev->ignore_connect_event = true;
return;
} else if (ret > 0)
status_code = (uint16_t)ret;
goto assoc_failed;
}
if (!netdev_ft_process_associate(netdev, frame, frame_len,
@ -2478,6 +2516,7 @@ static void netdev_associate_event(struct l_genl_msg *msg,
if (status_code != 0)
goto assoc_failed;
auth_complete:
if (netdev->sm) {
/*
* Start processing EAPoL frames now that the state machine
@ -2786,44 +2825,6 @@ static void netdev_fils_tx_associate(struct iovec *iov, size_t iov_len,
}
}
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,
@ -2958,8 +2959,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);
else
auth_proto_start(netdev->ap);
return 0;
}
@ -2993,9 +2994,9 @@ int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
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->ap = fils_sm_new(hs, netdev_fils_tx_authenticate,
netdev_fils_tx_associate,
netdev_fils_complete, netdev);
netdev);
break;
default:
cmd_connect = netdev_build_cmd_connect(netdev, bss, hs, NULL);