Discard unencrypted EAPOL-EAP when TK is set and PMF is enabled

RSN design is supposed to encrypt all Data frames, including EAPOL
frames, once the TK has been configured. However, there are deployed
implementations that do not really follow this design and there are
various examples from the older uses of EAPOL frame where those frames
were not encrypted. As such, strict filtering of unencrypted EAPOL
frames might results in undesired interoperation issues.

However, some of the most important cases of missing EAPOL frame
encryption should be possible to handle without causing too significant
issues. These are for cases where an attacker could potentially cause an
existing association to be dropped when PMF is used. EAP-Request is one
potential candidate for such attacks since that frame could be used to
initiate a new EAP authentication and the AP/Authenticator might not
allow that to complete or a large number of EAP-Request frames could be
injected to exceed the maximum number of EAP frames. Such an attack
could result in the station ending up disconnecting or at minimum,
getting into somewhat mismatching state with the AP.

Drop EAPOL-EAP frames when it is known that it was not encrypted but
should have been and when PMF is enabled. While it would be correct to
drop this even without PMF, that does not provide any significant
benefit since it is trivial to force disconnection in no-PMF cases. It
should also be noted that not all drivers provide information about the
encryption status of the EAPOL frames and this change has no impact with
drivers that do not indicate whether the frame was encrypted.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2022-05-07 17:42:51 +03:00
parent 872a57500c
commit 3c2fbe9f56
3 changed files with 27 additions and 0 deletions

View file

@ -1296,6 +1296,14 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
if (sm == NULL) if (sm == NULL)
return 0; return 0;
if (encrypted == FRAME_NOT_ENCRYPTED && sm->ctx->encryption_required &&
sm->ctx->encryption_required(sm->ctx->ctx)) {
wpa_printf(MSG_DEBUG,
"EAPOL: Discard unencrypted EAPOL frame when encryption since encryption was expected");
return 0;
}
sm->dot1xSuppEapolFramesRx++; sm->dot1xSuppEapolFramesRx++;
if (len < sizeof(*hdr)) { if (len < sizeof(*hdr)) {
sm->dot1xSuppInvalidEapolFramesRx++; sm->dot1xSuppInvalidEapolFramesRx++;

View file

@ -307,6 +307,13 @@ struct eapol_ctx {
* Automatically triggers a reconnect when not. * Automatically triggers a reconnect when not.
*/ */
int (*confirm_auth_cb)(void *ctx); int (*confirm_auth_cb)(void *ctx);
/**
* encryption_required - Check whether encryption is required
* @ctx: eapol_ctx from eap_peer_sm_init() call
* Returns: Whether the current session requires encryption
*/
bool (*encryption_required)(void *ctx);
}; };

View file

@ -1157,6 +1157,17 @@ static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len)
} }
} }
} }
static bool wpas_encryption_required(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
return wpa_s->wpa &&
wpa_sm_has_ptk_installed(wpa_s->wpa) &&
wpa_sm_pmf_enabled(wpa_s->wpa);
}
#endif /* IEEE8021X_EAPOL */ #endif /* IEEE8021X_EAPOL */
@ -1203,6 +1214,7 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
ctx->eap_error_cb = wpa_supplicant_eap_error_cb; ctx->eap_error_cb = wpa_supplicant_eap_error_cb;
ctx->confirm_auth_cb = wpa_supplicant_eap_auth_start_cb; ctx->confirm_auth_cb = wpa_supplicant_eap_auth_start_cb;
ctx->set_anon_id = wpa_supplicant_set_anon_id; ctx->set_anon_id = wpa_supplicant_set_anon_id;
ctx->encryption_required = wpas_encryption_required;
ctx->cb_ctx = wpa_s; ctx->cb_ctx = wpa_s;
wpa_s->eapol = eapol_sm_init(ctx); wpa_s->eapol = eapol_sm_init(ctx);
if (wpa_s->eapol == NULL) { if (wpa_s->eapol == NULL) {