mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-12-26 09:32:34 +01:00
fils: netdev: update to use auth_proto concepts
This commit is contained in:
parent
f0de2516ea
commit
8317b96e7d
122
src/fils.c
122
src/fils.c
@ -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;
|
||||
}
|
||||
|
14
src/fils.h
14
src/fils.h
@ -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);
|
||||
|
107
src/netdev.c
107
src/netdev.c
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user