From c67d5189a405b2dca015f47f31c55ba38a0d61eb Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 23 May 2024 20:16:40 +0200 Subject: [PATCH] hostapd: add support for authenticating with multiple PSKs via ubus helper Also supports assigning a VLAN ID based on the PSK Signed-off-by: Felix Fietkau --- .../network/services/hostapd/files/hostapd.uc | 26 +++- .../services/hostapd/files/wpad_acl.json | 2 +- .../hostapd/patches/601-ucode_support.patch | 135 ++++++++++++++++++ .../hostapd/patches/730-ft_iface.patch | 2 +- .../services/hostapd/src/src/ap/ucode.c | 105 ++++++++++++++ .../services/hostapd/src/src/ap/ucode.h | 9 ++ 6 files changed, 276 insertions(+), 3 deletions(-) diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc index dfddf8185b..3271962e16 100644 --- a/package/network/services/hostapd/files/hostapd.uc +++ b/package/network/services/hostapd/files/hostapd.uc @@ -833,6 +833,8 @@ let main_obj = { hostapd.printf(`Set new config for phy ${phy}: ${file}`); iface_set_config(phy, config); + hostapd.data.auth_obj.notify("reload", { phy }); + return { pid: hostapd.getpid() }; @@ -871,6 +873,10 @@ let main_obj = { hostapd.data.ubus = ubus; hostapd.data.obj = ubus.publish("hostapd", main_obj); + + +let auth_obj = {}; +hostapd.data.auth_obj = ubus.publish("hostapd-auth", auth_obj); hostapd.udebug_set("hostapd", hostapd.data.ubus); function bss_event(type, name, data) { @@ -897,5 +903,23 @@ return { }, bss_remove: function(name, obj) { bss_event("remove", name); - } + }, + sta_auth: function(iface, sta) { + let msg = { iface, sta }; + let ret = {}; + let data_cb = (type, data) => { + ret = { ...ret, ...data }; + }; + hostapd.data.auth_obj.notify("sta_auth", msg, data_cb, null, null, 1000); + return ret; + }, + sta_connected: function(iface, sta, data) { + let msg = { iface, sta, ...data }; + let ret = {}; + let data_cb = (type, data) => { + ret = { ...ret, ...data }; + }; + hostapd.data.auth_obj.notify("sta_connected", msg, data_cb, null, null, 1000); + return ret; + }, }; diff --git a/package/network/services/hostapd/files/wpad_acl.json b/package/network/services/hostapd/files/wpad_acl.json index 7532953cab..755f836b67 100644 --- a/package/network/services/hostapd/files/wpad_acl.json +++ b/package/network/services/hostapd/files/wpad_acl.json @@ -15,6 +15,6 @@ } }, "subscribe": [ "udebug" ], - "publish": [ "hostapd", "hostapd.*", "wpa_supplicant", "wpa_supplicant.*" ], + "publish": [ "hostapd", "hostapd.*", "wpa_supplicant", "wpa_supplicant.*", "hostapd-auth" ], "send": [ "bss.*", "wps_credentials" ] } diff --git a/package/network/services/hostapd/patches/601-ucode_support.patch b/package/network/services/hostapd/patches/601-ucode_support.patch index b826363248..61b6082c4d 100644 --- a/package/network/services/hostapd/patches/601-ucode_support.patch +++ b/package/network/services/hostapd/patches/601-ucode_support.patch @@ -678,3 +678,138 @@ as adding/removing interfaces. #ifdef CONFIG_MATCH_IFACE int matched; #endif /* CONFIG_MATCH_IFACE */ +--- a/src/ap/ieee802_11.c ++++ b/src/ap/ieee802_11.c +@@ -548,12 +548,17 @@ const char * sae_get_password(struct hos + struct sae_pt **s_pt, + const struct sae_pk **s_pk) + { ++ struct hostapd_bss_config *conf = hapd->conf; ++ struct hostapd_ssid *ssid = &conf->ssid; + const char *password = NULL; +- struct sae_password_entry *pw; ++ struct sae_password_entry *pw = NULL; + struct sae_pt *pt = NULL; + const struct sae_pk *pk = NULL; + struct hostapd_sta_wpa_psk_short *psk = NULL; + ++ if (sta && sta->use_sta_psk) ++ goto use_sta_psk; ++ + for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { + if (!is_broadcast_ether_addr(pw->peer_addr) && + (!sta || +@@ -575,12 +580,28 @@ const char * sae_get_password(struct hos + pt = hapd->conf->ssid.pt; + } + ++use_sta_psk: + if (!password && sta) { + for (psk = sta->psk; psk; psk = psk->next) { +- if (psk->is_passphrase) { +- password = psk->passphrase; ++ if (!psk->is_passphrase) ++ continue; ++ ++ password = psk->passphrase; ++ if (!sta->use_sta_psk) ++ break; ++ ++ if (sta->sae_pt) { ++ pt = sta->sae_pt; + break; + } ++ ++ pt = sae_derive_pt(conf->sae_groups, ssid->ssid, ++ ssid->ssid_len, ++ (const u8 *) password, ++ os_strlen(password), ++ NULL); ++ sta->sae_pt = pt; ++ break; + } + } + +@@ -3123,6 +3144,12 @@ static void handle_auth(struct hostapd_d + goto fail; + } + ++ res = hostapd_ucode_sta_auth(hapd, sta); ++ if (res) { ++ resp = res; ++ goto fail; ++ } ++ + sta->flags &= ~WLAN_STA_PREAUTH; + ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); + +--- a/src/ap/sta_info.c ++++ b/src/ap/sta_info.c +@@ -430,6 +430,9 @@ void ap_free_sta(struct hostapd_data *ha + forced_memzero(sta->last_tk, WPA_TK_MAX_LEN); + #endif /* CONFIG_TESTING_OPTIONS */ + ++ if (sta->sae_pt) ++ sae_deinit_pt(sta->sae_pt); ++ + os_free(sta); + } + +@@ -1434,6 +1437,8 @@ void ap_sta_set_authorized_event(struct + #endif /* CONFIG_P2P */ + const u8 *ip_ptr = NULL; + ++ if (authorized) ++ hostapd_ucode_sta_connected(hapd, sta); + #ifdef CONFIG_P2P + if (hapd->p2p_group == NULL) { + if (sta->p2p_ie != NULL && +--- a/src/ap/sta_info.h ++++ b/src/ap/sta_info.h +@@ -195,6 +195,9 @@ struct sta_info { + int vlan_id_bound; /* updated by ap_sta_bind_vlan() */ + /* PSKs from RADIUS authentication server */ + struct hostapd_sta_wpa_psk_short *psk; ++ struct sae_pt *sae_pt; ++ int use_sta_psk; ++ int psk_idx; + + char *identity; /* User-Name from RADIUS */ + char *radius_cui; /* Chargeable-User-Identity from RADIUS */ +--- a/src/ap/wpa_auth_glue.c ++++ b/src/ap/wpa_auth_glue.c +@@ -347,6 +347,7 @@ static const u8 * hostapd_wpa_auth_get_p + struct sta_info *sta = ap_get_sta(hapd, addr); + const u8 *psk; + ++ sta->psk_idx = 0; + if (vlan_id) + *vlan_id = 0; + if (psk_len) +@@ -393,13 +394,16 @@ static const u8 * hostapd_wpa_auth_get_p + * returned psk which should not be returned again. + * logic list (all hostapd_get_psk; all sta->psk) + */ ++ if (sta && sta->use_sta_psk) ++ psk = NULL; + if (sta && sta->psk && !psk) { + struct hostapd_sta_wpa_psk_short *pos; ++ int psk_idx = 1; + + if (vlan_id) + *vlan_id = 0; + psk = sta->psk->psk; +- for (pos = sta->psk; pos; pos = pos->next) { ++ for (pos = sta->psk; pos; pos = pos->next, psk_idx++) { + if (pos->is_passphrase) { + if (pbkdf2_sha1(pos->passphrase, + hapd->conf->ssid.ssid, +@@ -416,6 +420,8 @@ static const u8 * hostapd_wpa_auth_get_p + break; + } + } ++ if (psk) ++ sta->psk_idx = psk_idx; + } + return psk; + } diff --git a/package/network/services/hostapd/patches/730-ft_iface.patch b/package/network/services/hostapd/patches/730-ft_iface.patch index 2f47f17d96..1fc4fd28f5 100644 --- a/package/network/services/hostapd/patches/730-ft_iface.patch +++ b/package/network/services/hostapd/patches/730-ft_iface.patch @@ -29,7 +29,7 @@ a VLAN interface on top of the bridge, instead of using the bridge directly int bridge_hairpin; /* hairpin_mode on bridge members */ --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c -@@ -1777,8 +1777,12 @@ int hostapd_setup_wpa(struct hostapd_dat +@@ -1783,8 +1783,12 @@ int hostapd_setup_wpa(struct hostapd_dat wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) { const char *ft_iface; diff --git a/package/network/services/hostapd/src/src/ap/ucode.c b/package/network/services/hostapd/src/src/ap/ucode.c index d344190208..d07851473a 100644 --- a/package/network/services/hostapd/src/src/ap/ucode.c +++ b/package/network/services/hostapd/src/src/ap/ucode.c @@ -9,6 +9,7 @@ #include "ap_drv_ops.h" #include "dfs.h" #include "acs.h" +#include "ieee802_11_auth.h" #include static uc_resource_type_t *global_type, *bss_type, *iface_type; @@ -701,6 +702,110 @@ out: return ret ? NULL : ucv_boolean_new(true); } +int hostapd_ucode_sta_auth(struct hostapd_data *hapd, struct sta_info *sta) +{ + char addr[sizeof(MACSTR)]; + uc_value_t *val, *cur; + int ret = 0; + + if (wpa_ucode_call_prepare("sta_auth")) + return 0; + + uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface))); + + snprintf(addr, sizeof(addr), MACSTR, MAC2STR(sta->addr)); + val = ucv_string_new(addr); + uc_value_push(ucv_get(val)); + + val = wpa_ucode_call(2); + + cur = ucv_object_get(val, "psk", NULL); + if (ucv_type(cur) == UC_ARRAY) { + struct hostapd_sta_wpa_psk_short *p, **next; + size_t len = ucv_array_length(cur); + + next = &sta->psk; + hostapd_free_psk_list(*next); + *next = NULL; + + for (size_t i = 0; i < len; i++) { + uc_value_t *cur_psk; + const char *str; + size_t str_len; + + cur_psk = ucv_array_get(cur, i); + str = ucv_string_get(cur_psk); + str_len = strlen(str); + if (!str || str_len < 8 || str_len > 64) + continue; + + p = os_zalloc(sizeof(*p)); + if (len == 64) { + if (hexstr2bin(str, p->psk, PMK_LEN) < 0) { + free(p); + continue; + } + } else { + p->is_passphrase = 1; + memcpy(p->passphrase, str, str_len + 1); + } + + *next = p; + next = &p->next; + } + } + + cur = ucv_object_get(val, "force_psk", NULL); + sta->use_sta_psk = ucv_is_truish(cur); + + cur = ucv_object_get(val, "status", NULL); + if (ucv_type(cur) == UC_INTEGER) + ret = ucv_int64_get(cur); + + ucv_put(val); + ucv_gc(vm); + + return ret; +} + +void hostapd_ucode_sta_connected(struct hostapd_data *hapd, struct sta_info *sta) +{ + char addr[sizeof(MACSTR)]; + uc_value_t *val, *cur; + int ret = 0; + + if (wpa_ucode_call_prepare("sta_connected")) + return; + + uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface))); + + snprintf(addr, sizeof(addr), MACSTR, MAC2STR(sta->addr)); + val = ucv_string_new(addr); + uc_value_push(ucv_get(val)); + + val = ucv_object_new(vm); + if (sta->psk_idx) + ucv_object_add(val, "psk_idx", ucv_int64_new(sta->psk_idx - 1)); + uc_value_push(ucv_get(val)); + + val = wpa_ucode_call(3); + if (ucv_type(val) != UC_OBJECT) + goto out; + + cur = ucv_object_get(val, "vlan", NULL); + if (ucv_type(cur) == UC_INTEGER) { + struct vlan_description vdesc = { + .notempty = 1, + .untagged = ucv_int64_get(cur), + }; + + ap_sta_set_vlan(hapd, sta, &vdesc); + ap_sta_bind_vlan(hapd, sta); + } + +out: + ucv_put(val); +} int hostapd_ucode_init(struct hapd_interfaces *ifaces) { diff --git a/package/network/services/hostapd/src/src/ap/ucode.h b/package/network/services/hostapd/src/src/ap/ucode.h index d00b787169..ff6dea3548 100644 --- a/package/network/services/hostapd/src/src/ap/ucode.h +++ b/package/network/services/hostapd/src/src/ap/ucode.h @@ -23,6 +23,8 @@ int hostapd_ucode_init(struct hapd_interfaces *ifaces); void hostapd_ucode_free(void); void hostapd_ucode_free_iface(struct hostapd_iface *iface); +int hostapd_ucode_sta_auth(struct hostapd_data *hapd, struct sta_info *sta); +void hostapd_ucode_sta_connected(struct hostapd_data *hapd, struct sta_info *sta); void hostapd_ucode_add_bss(struct hostapd_data *hapd); void hostapd_ucode_free_bss(struct hostapd_data *hapd); void hostapd_ucode_reload_bss(struct hostapd_data *hapd); @@ -42,6 +44,13 @@ static inline void hostapd_ucode_free_iface(struct hostapd_iface *iface) static inline void hostapd_ucode_reload_bss(struct hostapd_data *hapd) { } +static inline int hostapd_ucode_sta_auth(struct hostapd_data *hapd, struct sta_info *sta) +{ + return 0; +} +static inline void hostapd_ucode_sta_connected(struct hostapd_data *hapd, struct sta_info *sta) +{ +} static inline void hostapd_ucode_add_bss(struct hostapd_data *hapd) { } -- 2.30.2