From 1c3438fec4bad13a676617915ff56af54e7b4542 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sat, 2 Apr 2022 13:12:43 +0300 Subject: [PATCH] RADIUS ACL/PSK check during 4-way handshake Add an alternative sequence for performing the RADIUS ACL check and PSK fetch. The previously used (macaddr_acl=2, wpa_psk_radius=2) combination does this during IEEE 802.11 Authentication frame exchange while the new option (wpa_psk_radius=3) does this during the 4-way handshake. This allows some more information to be provided to the RADIUS authentication server. Signed-off-by: Jouni Malinen --- hostapd/config_file.c | 3 +- hostapd/hostapd.conf | 5 ++- src/ap/ap_config.c | 4 ++- src/ap/ap_config.h | 5 +-- src/ap/ieee802_11.c | 5 ++- src/ap/ieee802_11.h | 2 ++ src/ap/ieee802_11_auth.c | 76 ++++++++++++++++++++++++++++++++++++---- src/ap/ieee802_11_auth.h | 5 ++- src/ap/wpa_auth.c | 51 ++++++++++++++++++++++++++- src/ap/wpa_auth.h | 9 ++++- src/ap/wpa_auth_glue.c | 25 ++++++++++++- src/ap/wpa_auth_i.h | 1 + 12 files changed, 172 insertions(+), 19 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index a45fadbaa..442c757f1 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2929,7 +2929,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->wpa_psk_radius = atoi(pos); if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED && bss->wpa_psk_radius != PSK_RADIUS_ACCEPTED && - bss->wpa_psk_radius != PSK_RADIUS_REQUIRED) { + bss->wpa_psk_radius != PSK_RADIUS_REQUIRED && + bss->wpa_psk_radius != PSK_RADIUS_DURING_4WAY_HS) { wpa_printf(MSG_ERROR, "Line %d: unknown wpa_psk_radius %d", line, bss->wpa_psk_radius); diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 9b0c809ea..71c11989e 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1655,12 +1655,15 @@ own_ip_addr=127.0.0.1 #wpa_psk_file=/etc/hostapd.wpa_psk # Optionally, WPA passphrase can be received from RADIUS authentication server -# This requires macaddr_acl to be set to 2 (RADIUS) +# This requires macaddr_acl to be set to 2 (RADIUS) for wpa_psk_radius values +# 1 and 2. # 0 = disabled (default) # 1 = optional; use default passphrase/psk if RADIUS server does not include # Tunnel-Password # 2 = required; reject authentication if RADIUS server does not include # Tunnel-Password +# 3 = ask RADIUS server during 4-way handshake if there is no locally +# configured PSK/passphrase for the STA #wpa_psk_radius=0 # Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index efbb87b9f..f2d13ea71 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -1,6 +1,6 @@ /* * hostapd / Configuration helper functions - * Copyright (c) 2003-2014, Jouni Malinen + * Copyright (c) 2003-2022, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -1243,6 +1243,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, if (full_config && bss->wpa && bss->wpa_psk_radius != PSK_RADIUS_IGNORED && + bss->wpa_psk_radius != PSK_RADIUS_DURING_4WAY_HS && bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) { wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no " "RADIUS checking (macaddr_acl=2) enabled."); @@ -1253,6 +1254,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, wpa_key_mgmt_wpa_psk_no_sae(bss->wpa_key_mgmt) && bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL && bss->ssid.wpa_psk_file == NULL && + bss->wpa_psk_radius != PSK_RADIUS_DURING_4WAY_HS && (bss->wpa_psk_radius != PSK_RADIUS_REQUIRED || bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) { wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase " diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 5a4c98f34..4b37a5c59 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -1,6 +1,6 @@ /* * hostapd / Configuration definitions and helpers functions - * Copyright (c) 2003-2015, Jouni Malinen + * Copyright (c) 2003-2022, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -364,7 +364,8 @@ struct hostapd_bss_config { enum { PSK_RADIUS_IGNORED = 0, PSK_RADIUS_ACCEPTED = 1, - PSK_RADIUS_REQUIRED = 2 + PSK_RADIUS_REQUIRED = 2, + PSK_RADIUS_DURING_4WAY_HS = 3, } wpa_psk_radius; int wpa_pairwise; int group_cipher; /* wpa_group value override from configuation */ diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 147b0a670..c722242eb 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -2325,9 +2325,8 @@ static int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, } -static int -ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, - int res, struct radius_sta *info) +int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, + int res, struct radius_sta *info) { u32 session_timeout = info->session_timeout; u32 acct_interim_interval = info->acct_interim_interval; diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 923cd520b..ccc8d8f5d 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -198,5 +198,7 @@ u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ext_capab_ie, size_t ext_capab_ie_len); size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type); u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type); +int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, + int res, struct radius_sta *info); #endif /* IEEE802_11_H */ diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index cf13008a1..b9d9ea119 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.11 authentication (ACL) - * Copyright (c) 2003-2012, Jouni Malinen + * Copyright (c) 2003-2022, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -20,6 +20,8 @@ #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "sta_info.h" +#include "wpa_auth.h" #include "ieee802_11.h" #include "ieee802_1x.h" #include "ieee802_11_auth.h" @@ -43,6 +45,8 @@ struct hostapd_acl_query_data { u8 *auth_msg; /* IEEE 802.11 authentication frame from station */ size_t auth_msg_len; struct hostapd_acl_query_data *next; + bool radius_psk; + int akm; }; @@ -153,6 +157,13 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, goto fail; } + if (query->akm && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE, + wpa_akm_to_suite(query->akm))) { + wpa_printf(MSG_DEBUG, "Could not add WLAN-AKM-Suite"); + goto fail; + } + if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0) goto fail; return 0; @@ -557,17 +568,40 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, cache->next = hapd->acl_cache; hapd->acl_cache = cache; + if (query->radius_psk) { + struct sta_info *sta; + bool success = cache->accepted == HOSTAPD_ACL_ACCEPT; + + sta = ap_get_sta(hapd, query->addr); + if (!sta || !sta->wpa_sm) { + wpa_printf(MSG_DEBUG, + "No STA/SM entry found for the RADIUS PSK response"); + goto done; + } +#ifdef NEED_AP_MLME + if (success && + (ieee802_11_set_radius_info(hapd, sta, cache->accepted, + info) < 0 || + ap_sta_bind_vlan(hapd, sta) < 0)) + success = false; +#endif /* NEED_AP_MLME */ + wpa_auth_sta_radius_psk_resp(sta->wpa_sm, success); + } else { #ifdef CONFIG_DRIVER_RADIUS_ACL - hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted, - info->session_timeout); + hostapd_drv_set_radius_acl_auth(hapd, query->addr, + cache->accepted, + info->session_timeout); #else /* CONFIG_DRIVER_RADIUS_ACL */ #ifdef NEED_AP_MLME - /* Re-send original authentication frame for 802.11 processing */ - wpa_printf(MSG_DEBUG, "Re-sending authentication frame after " - "successful RADIUS ACL query"); - ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL); + /* Re-send original authentication frame for 802.11 processing + */ + wpa_printf(MSG_DEBUG, + "Re-sending authentication frame after successful RADIUS ACL query"); + ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, + NULL); #endif /* NEED_AP_MLME */ #endif /* CONFIG_DRIVER_RADIUS_ACL */ + } done: if (prev == NULL) @@ -649,3 +683,31 @@ void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk) bin_clear_free(prev, sizeof(*prev)); } } + + +#ifndef CONFIG_NO_RADIUS +void hostapd_acl_req_radius_psk(struct hostapd_data *hapd, const u8 *addr, + int key_mgmt, const u8 *anonce, + const u8 *eapol, size_t eapol_len) +{ + struct hostapd_acl_query_data *query; + + query = os_zalloc(sizeof(*query)); + if (!query) + return; + + query->radius_psk = true; + query->akm = key_mgmt; + os_get_reltime(&query->timestamp); + os_memcpy(query->addr, addr, ETH_ALEN); + if (hostapd_radius_acl_query(hapd, addr, query)) { + wpa_printf(MSG_DEBUG, + "Failed to send Access-Request for RADIUS PSK/ACL query"); + hostapd_acl_query_free(query); + return; + } + + query->next = hapd->acl_queries; + hapd->acl_queries = query; +} +#endif /* CONFIG_NO_RADIUS */ diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h index 9410f55c5..22ae1a9d3 100644 --- a/src/ap/ieee802_11_auth.h +++ b/src/ap/ieee802_11_auth.h @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.11 authentication (ACL) - * Copyright (c) 2003-2005, Jouni Malinen + * Copyright (c) 2003-2022, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -36,5 +36,8 @@ void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk); void hostapd_acl_expire(struct hostapd_data *hapd); void hostapd_copy_psk_list(struct hostapd_sta_wpa_psk_short **psk, struct hostapd_sta_wpa_psk_short *src); +void hostapd_acl_req_radius_psk(struct hostapd_data *hapd, const u8 *addr, + int key_mgmt, const u8 *anonce, + const u8 *eapol, size_t eapol_len); #endif /* IEEE802_11_AUTH_H */ diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 6c58c2113..7da102e90 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1,6 +1,6 @@ /* * IEEE 802.11 RSN / WPA Authenticator - * Copyright (c) 2004-2019, Jouni Malinen + * Copyright (c) 2004-2022, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -1481,6 +1481,12 @@ static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx) struct wpa_authenticator *wpa_auth = eloop_ctx; struct wpa_state_machine *sm = timeout_ctx; + if (sm->waiting_radius_psk) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "Ignore EAPOL-Key timeout while waiting for RADIUS PSK"); + return; + } + sm->pending_1_of_4_timeout = 0; wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout"); sm->TimeoutEvt = true; @@ -3017,6 +3023,19 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) break; } + if (!ok && wpa_key_mgmt_wpa_psk_no_sae(sm->wpa_key_mgmt) && + wpa_auth->conf.radius_psk && wpa_auth->cb->request_radius_psk && + !sm->waiting_radius_psk) { + wpa_printf(MSG_DEBUG, "No PSK available - ask RADIUS server"); + wpa_auth->cb->request_radius_psk(wpa_auth->cb_ctx, sm->addr, + sm->wpa_key_mgmt, + sm->ANonce, + sm->last_rx_eapol_key, + sm->last_rx_eapol_key_len); + sm->waiting_radius_psk = 1; + return; + } + if (!ok) { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "invalid MIC in msg 2/4 of 4-Way Handshake"); @@ -3776,6 +3795,11 @@ SM_STEP(WPA_PTK) } else if (wpa_auth_uses_sae(sm) && sm->pmksa) { SM_ENTER(WPA_PTK, PTKSTART); #endif /* CONFIG_SAE */ + } else if (wpa_key_mgmt_wpa_psk_no_sae(sm->wpa_key_mgmt) && + wpa_auth->conf.radius_psk) { + wpa_printf(MSG_DEBUG, + "INITPSK: No PSK yet available for STA - use RADIUS later"); + SM_ENTER(WPA_PTK, PTKSTART); } else { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "no PSK configured for the STA"); @@ -5689,3 +5713,28 @@ void wpa_auth_set_ocv_override_freq(struct wpa_authenticator *wpa_auth, } #endif /* CONFIG_TESTING_OPTIONS */ + + +void wpa_auth_sta_radius_psk_resp(struct wpa_state_machine *sm, bool success) +{ + if (!sm->waiting_radius_psk) { + wpa_printf(MSG_DEBUG, + "Ignore RADIUS PSK response for " MACSTR + " that did not wait one", + MAC2STR(sm->addr)); + return; + } + + wpa_printf(MSG_DEBUG, "RADIUS PSK response for " MACSTR " (%s)", + MAC2STR(sm->addr), success ? "success" : "fail"); + sm->waiting_radius_psk = 0; + + if (success) { + /* Try to process the EAPOL-Key msg 2/4 again */ + sm->EAPOLKeyReceived = true; + } else { + sm->Disconnect = true; + } + + eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL); +} diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index fe47723b9..d2a36006a 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -1,6 +1,6 @@ /* * hostapd - IEEE 802.11i-2004 / WPA Authenticator - * Copyright (c) 2004-2017, Jouni Malinen + * Copyright (c) 2004-2022, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -273,6 +273,8 @@ struct wpa_auth_config { * PTK derivation regardless of advertised capabilities. */ bool force_kdk_derivation; + + bool radius_psk; }; typedef enum { @@ -320,6 +322,9 @@ struct wpa_auth_callbacks { void (*store_ptksa)(void *ctx, const u8 *addr, int cipher, u32 life_time, const struct wpa_ptk *ptk); void (*clear_ptksa)(void *ctx, const u8 *addr, int cipher); + void (*request_radius_psk)(void *ctx, const u8 *addr, int key_mgmt, + const u8 *anonce, + const u8 *eapol, size_t eapol_len); #ifdef CONFIG_IEEE80211R_AP struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); int (*add_sta_ft)(void *ctx, const u8 *sta_addr); @@ -572,4 +577,6 @@ void wpa_auth_set_ocv_override_freq(struct wpa_authenticator *wpa_auth, enum wpa_auth_ocv_override_frame frame, unsigned int freq); +void wpa_auth_sta_radius_psk_resp(struct wpa_state_machine *sm, bool success); + #endif /* WPA_AUTH_H */ diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 1103a48d7..71a487161 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -1,6 +1,6 @@ /* * hostapd / WPA authenticator glue code - * Copyright (c) 2002-2012, Jouni Malinen + * Copyright (c) 2002-2022, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -29,6 +29,7 @@ #include "ap_drv_ops.h" #include "ap_config.h" #include "ieee802_11.h" +#include "ieee802_11_auth.h" #include "pmksa_cache_auth.h" #include "wpa_auth.h" #include "wpa_auth_glue.h" @@ -214,6 +215,8 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->force_kdk_derivation = conf->force_kdk_derivation; #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_PASN */ + + wconf->radius_psk = conf->wpa_psk_radius == PSK_RADIUS_DURING_4WAY_HS; } @@ -1443,6 +1446,23 @@ static void hostapd_wpa_unregister_ft_oui(struct hostapd_data *hapd) #endif /* CONFIG_IEEE80211R_AP */ +#ifndef CONFIG_NO_RADIUS +static void hostapd_request_radius_psk(void *ctx, const u8 *addr, int key_mgmt, + const u8 *anonce, + const u8 *eapol, size_t eapol_len) +{ + struct hostapd_data *hapd = ctx; + + wpa_printf(MSG_DEBUG, "RADIUS PSK request for " MACSTR " key_mgmt=0x%x", + MAC2STR(addr), key_mgmt); + wpa_hexdump(MSG_DEBUG, "ANonce", anonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "EAPOL", eapol, eapol_len); + hostapd_acl_req_radius_psk(hapd, addr, key_mgmt, anonce, eapol, + eapol_len); +} +#endif /* CONFIG_NO_RADIUS */ + + int hostapd_setup_wpa(struct hostapd_data *hapd) { struct wpa_auth_config _conf; @@ -1486,6 +1506,9 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) .set_session_timeout = hostapd_wpa_auth_set_session_timeout, .get_session_timeout = hostapd_wpa_auth_get_session_timeout, #endif /* CONFIG_IEEE80211R_AP */ +#ifndef CONFIG_NO_RADIUS + .request_radius_psk = hostapd_request_radius_psk, +#endif /* CONFIG_NO_RADIUS */ }; const u8 *wpa_ie; size_t wpa_ie_len; diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index a6dc1a591..7e93e5ab8 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -89,6 +89,7 @@ struct wpa_state_machine { unsigned int rx_eapol_key_secure:1; unsigned int update_snonce:1; unsigned int alt_snonce_valid:1; + unsigned int waiting_radius_psk:1; #ifdef CONFIG_IEEE80211R_AP unsigned int ft_completed:1; unsigned int pmk_r1_name_valid:1;