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

View File

@ -60,6 +60,7 @@
#include "src/nl80211util.h" #include "src/nl80211util.h"
#include "src/owe.h" #include "src/owe.h"
#include "src/fils.h" #include "src/fils.h"
#include "src/auth-proto.h"
#ifndef ENOTSUPP #ifndef ENOTSUPP
#define ENOTSUPP 524 #define ENOTSUPP 524
@ -97,7 +98,7 @@ struct netdev {
struct eapol_sm *sm; struct eapol_sm *sm;
struct sae_sm *sae_sm; struct sae_sm *sae_sm;
struct owe_sm *owe; struct owe_sm *owe;
struct fils_sm *fils; struct auth_proto *ap;
struct handshake_state *handshake; struct handshake_state *handshake;
uint32_t connect_cmd_id; uint32_t connect_cmd_id;
uint32_t disconnect_cmd_id; uint32_t disconnect_cmd_id;
@ -561,6 +562,11 @@ static void netdev_connect_free(struct netdev *netdev)
netdev->owe = NULL; netdev->owe = NULL;
} }
if (netdev->ap) {
auth_proto_free(netdev->ap);
netdev->ap = NULL;
}
eapol_preauth_cancel(netdev->index); eapol_preauth_cancel(netdev->index);
if (netdev->handshake) { if (netdev->handshake) {
@ -2350,6 +2356,8 @@ static void netdev_authenticate_event(struct l_genl_msg *msg,
const void *data; const void *data;
const uint8_t *frame = NULL; const uint8_t *frame = NULL;
size_t frame_len = 0; size_t frame_len = 0;
int ret;
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
l_debug(""); 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 * the FT Associate command is included in the attached frame and is
* not available in the Authenticate command callback. * 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; return;
if (!l_genl_attr_init(&attr, msg)) { 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) { if (netdev->sae_sm) {
sae_timeout(netdev->sae_sm); sae_timeout(netdev->sae_sm);
return; return;
} } else if (auth_proto_auth_timeout(netdev->ap))
return;
goto auth_error; goto auth_error;
@ -2410,16 +2419,22 @@ static void netdev_authenticate_event(struct l_genl_msg *msg,
frame + 26, frame_len - 26); frame + 26, frame_len - 26);
else if (netdev->owe) else if (netdev->owe)
owe_rx_authenticate(netdev->owe); owe_rx_authenticate(netdev->owe);
else if (netdev->fils) else if (netdev->ap) {
fils_rx_authenticate(netdev->fils, frame, frame_len); ret = auth_proto_rx_authenticate(netdev->ap, frame, frame_len);
else if (ret == 0 || ret == -EAGAIN)
return;
else if (ret > 0)
status_code = (uint16_t)ret;
goto auth_error;
} else
goto auth_error; goto auth_error;
return; return;
auth_error: auth_error:
netdev_connect_failed(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED, netdev_connect_failed(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED,
MMPDU_STATUS_CODE_UNSPECIFIED); status_code);
} }
static void netdev_associate_event(struct l_genl_msg *msg, 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; size_t frame_len = 0;
const uint8_t *frame = NULL; const uint8_t *frame = NULL;
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED; uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
int ret;
l_debug(""); l_debug("");
@ -2438,7 +2454,7 @@ static void netdev_associate_event(struct l_genl_msg *msg,
return; return;
if (!netdev->owe && !netdev->in_ft && !netdev->handshake->mde && if (!netdev->owe && !netdev->in_ft && !netdev->handshake->mde &&
!netdev->fils) !netdev->ap)
return; return;
if (!l_genl_attr_init(&attr, msg)) { if (!l_genl_attr_init(&attr, msg)) {
@ -2450,6 +2466,10 @@ static void netdev_associate_event(struct l_genl_msg *msg,
switch (type) { switch (type) {
case NL80211_ATTR_TIMED_OUT: case NL80211_ATTR_TIMED_OUT:
l_warn("association timed out"); l_warn("association timed out");
if (auth_proto_assoc_timeout(netdev->ap))
return;
goto assoc_failed; goto assoc_failed;
case NL80211_ATTR_FRAME: case NL80211_ATTR_FRAME:
@ -2466,9 +2486,27 @@ static void netdev_associate_event(struct l_genl_msg *msg,
if (netdev->owe) { if (netdev->owe) {
owe_rx_associate(netdev->owe, frame, frame_len); owe_rx_associate(netdev->owe, frame, frame_len);
return; return;
} else if (netdev->fils) { } else if (netdev->ap) {
fils_rx_associate(netdev->fils, frame, frame_len); ret = auth_proto_rx_associate(netdev->ap, frame, frame_len);
return; 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, 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) if (status_code != 0)
goto assoc_failed; goto assoc_failed;
auth_complete:
if (netdev->sm) { if (netdev->sm) {
/* /*
* Start processing EAPoL frames now that the state machine * 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, static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev,
struct scan_bss *bss, struct scan_bss *bss,
struct handshake_state *hs, struct handshake_state *hs,
@ -2958,8 +2959,8 @@ static int netdev_connect_common(struct netdev *netdev,
sae_start(netdev->sae_sm); sae_start(netdev->sae_sm);
else if (netdev->owe) else if (netdev->owe)
owe_start(netdev->owe); owe_start(netdev->owe);
else if (netdev->fils) else
fils_start(netdev->fils); auth_proto_start(netdev->ap);
return 0; return 0;
} }
@ -2993,9 +2994,9 @@ int netdev_connect(struct netdev *netdev, struct scan_bss *bss,
break; break;
case IE_RSN_AKM_SUITE_FILS_SHA256: case IE_RSN_AKM_SUITE_FILS_SHA256:
case IE_RSN_AKM_SUITE_FILS_SHA384: 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_tx_associate,
netdev_fils_complete, netdev); netdev);
break; break;
default: default:
cmd_connect = netdev_build_cmd_connect(netdev, bss, hs, NULL); cmd_connect = netdev_build_cmd_connect(netdev, bss, hs, NULL);