[openwrt/openwrt] hostapd: add support for rxkh_file

LEDE Commits lede-commits at lists.infradead.org
Thu Dec 12 10:40:17 PST 2024


nbd pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/7306ae401cc2cf84210fb74f37aa17049bce2e9f

commit 7306ae401cc2cf84210fb74f37aa17049bce2e9f
Author: Sybil127 <sybil127 at outlook.com>
AuthorDate: Thu May 23 18:49:54 2024 +0200

    hostapd: add support for rxkh_file
    
    Initial support for dynamic reload of RxKHs.
    
    In order to check if RxKHs need reloading.
    RxKHs defined in the rxkh_file first has to be parsed and formated,
    the same way as hostapd will read from the file and also output,
    with the command GET_RXKHS.
    Then each list of RxKHs can be hashed and compared.
    
    Ucode implementation of hostapds rkh_derive_key() function.
    Hostapd converts hex keys with 128-bits or more when less than 256-bits
    to 256-bits, and truncates those that are more than 256-bits.
    See: https://w1.fi/cgit/hostap/commit/hostapd/config_file.c?id=245fc96e5f4b1c566b7eaa19180c774307ebed79
    
    Signed-off-by: Sybil127 <sybil127 at outlook.com>
    Signed-off-by: Felix Fietkau <nbd at nbd.name>
---
 package/network/services/hostapd/files/hostapd.uc  | 69 +++++++++++++++++++++-
 .../network/services/hostapd/src/src/ap/ucode.c    |  1 +
 .../network/services/hostapd/src/src/utils/ucode.c | 35 +++++++++++
 .../network/services/hostapd/src/src/utils/ucode.h |  1 +
 4 files changed, 104 insertions(+), 2 deletions(-)

diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc
index 053f08cb96..e345a678f4 100644
--- a/package/network/services/hostapd/files/hostapd.uc
+++ b/package/network/services/hostapd/files/hostapd.uc
@@ -11,6 +11,7 @@ hostapd.data.file_fields = {
 	vlan_file: true,
 	wpa_psk_file: true,
 	sae_password_file: true,
+	rxkh_file: true,
 	accept_mac_file: true,
 	deny_mac_file: true,
 	eap_user_file: true,
@@ -351,6 +352,64 @@ function bss_reload_psk(bss, config, old_config)
 	hostapd.printf(`Reload WPA PSK file for bss ${config.ifname}: ${ret}`);
 }
 
+function normalize_rxkhs(txt)
+{
+	const pat = {
+		sep: "\x20",
+		mac: "([[:xdigit:]]{2}:?){5}[[:xdigit:]]{2}",
+		r0kh_id: "[\x21-\x7e]{1,48}",
+		r1kh_id: "([[:xdigit:]]{2}:?){5}[[:xdigit:]]{2}",
+		key: "[[:xdigit:]]{32,}",
+		r0kh: function() {
+			return "r0kh=" + this.mac + this.sep + this.r0kh_id;
+		},
+		r1kh: function() {
+			return "r1kh=" + this.mac + this.sep + this.r1kh_id;
+		},
+		rxkh: function() {
+			return "(" + this.r0kh() + "|" + this.r1kh() + ")" + this.sep + this.key;
+		},
+	};
+
+	let rxkhs = filter(
+		split(txt, "\n"), (line) => match(line, regexp("^" + pat.rxkh() + "$"))
+	) ?? [];
+
+	rxkhs = map(rxkhs, function(k) {
+		k = split(k, " ", 3);
+		k[0] = lc(k[0]);
+		if(match(k[0], /^r1kh/)) {
+			k[1] = lc(k[1]);
+		}
+		if(!k[2] = hostapd.rkh_derive_key(k[2])) {
+			return;
+		}
+		return join(" ", k);
+	});
+
+	return join("\n", sort(filter(rxkhs, length)));
+}
+
+function bss_reload_rxkhs(bss, config, old_config)
+{
+	let bss_rxkhs = join("\n", sort(split(bss.ctrl("GET_RXKHS"), "\n")));
+	let bss_rxkhs_hash = hostapd.sha1(bss_rxkhs);
+
+	if (is_equal(config.hash.rxkh_file, bss_rxkhs_hash)) {
+		if (is_equal(old_config.hash.rxkh_file, config.hash.rxkh_file))
+			return;
+	}
+
+	old_config.hash.rxkh_file = config.hash.rxkh_file;
+	if (!is_equal(old_config, config))
+		return;
+
+	let ret = bss.ctrl("RELOAD_RXKHS");
+	ret ??= "failed";
+
+	hostapd.printf(`Reload RxKH file for bss ${config.ifname}: ${ret}`);
+}
+
 function remove_file_fields(config)
 {
 	return filter(config, (line) => !hostapd.data.file_fields[split(line, "=")[0]]);
@@ -652,6 +711,7 @@ function iface_reload_config(name, phydev, config, old_config)
 		}
 
 		bss_reload_psk(bss, config.bss[i], bss_list_cfg[i]);
+		bss_reload_rxkhs(bss, config.bss[i], bss_list_cfg[i]);
 		if (is_equal(config.bss[i], bss_list_cfg[i]))
 			continue;
 
@@ -780,8 +840,13 @@ function iface_load_config(phy, radio, filename)
 			continue;
 		}
 
-		if (hostapd.data.file_fields[val[0]])
-			bss.hash[val[0]] = hostapd.sha1(readfile(val[1]));
+		if (hostapd.data.file_fields[val[0]]) {
+			if (val[0] == "rxkh_file") {
+				bss.hash[val[0]] = hostapd.sha1(normalize_rxkhs(readfile(val[1])));
+			} else {
+				bss.hash[val[0]] = hostapd.sha1(readfile(val[1]));
+			}
+		}
 
 		push(bss.data, line);
 	}
diff --git a/package/network/services/hostapd/src/src/ap/ucode.c b/package/network/services/hostapd/src/src/ap/ucode.c
index 2da2b4dc93..adc7c41914 100644
--- a/package/network/services/hostapd/src/src/ap/ucode.c
+++ b/package/network/services/hostapd/src/src/ap/ucode.c
@@ -823,6 +823,7 @@ int hostapd_ucode_init(struct hapd_interfaces *ifaces)
 		{ "printf",	uc_wpa_printf },
 		{ "getpid", uc_wpa_getpid },
 		{ "sha1", uc_wpa_sha1 },
+		{ "rkh_derive_key", uc_wpa_rkh_derive_key },
 		{ "freq_info", uc_wpa_freq_info },
 		{ "add_iface", uc_hostapd_add_iface },
 		{ "remove_iface", uc_hostapd_remove_iface },
diff --git a/package/network/services/hostapd/src/src/utils/ucode.c b/package/network/services/hostapd/src/src/utils/ucode.c
index 29c753c326..50b87982ce 100644
--- a/package/network/services/hostapd/src/src/utils/ucode.c
+++ b/package/network/services/hostapd/src/src/utils/ucode.c
@@ -3,6 +3,7 @@
 #include "utils/eloop.h"
 #include "crypto/crypto.h"
 #include "crypto/sha1.h"
+#include "crypto/sha256.h"
 #include "common/ieee802_11_common.h"
 #include <linux/netlink.h>
 #include <linux/genetlink.h>
@@ -236,6 +237,40 @@ uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs)
 	return ucv_string_new_length(hash_hex, 2 * ARRAY_SIZE(hash));
 }
 
+uc_value_t *uc_wpa_rkh_derive_key(uc_vm_t *vm, size_t nargs)
+{
+	u8 oldkey[16];
+	char *oldkey_hex;
+	u8 key[SHA256_MAC_LEN];
+	size_t key_len = sizeof(key);
+	char key_hex[2 * ARRAY_SIZE(key) + 1];
+	uc_value_t *val = uc_fn_arg(0);
+	int i;
+
+	if (ucv_type(val) != UC_STRING)
+		return NULL;
+
+	oldkey_hex = ucv_string_get(val);
+
+	if (!hexstr2bin(oldkey_hex, key, key_len))
+		return ucv_string_new_length(oldkey_hex, 2 * ARRAY_SIZE(key));
+
+	if (hexstr2bin(oldkey_hex, oldkey, sizeof(oldkey))) {
+		wpa_printf(MSG_ERROR, "Invalid RxKH key: '%s'", oldkey_hex);
+		return NULL;
+	}
+
+	if (hmac_sha256_kdf(oldkey, sizeof(oldkey), "FT OLDKEY", NULL, 0, key, key_len) < 0) {
+		wpa_printf(MSG_ERROR, "Invalid RxKH key: '%s'", oldkey_hex);
+		return NULL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(key); i++)
+		sprintf(key_hex + 2 * i, "%02x", key[i]);
+
+	return ucv_string_new_length(key_hex, 2 * ARRAY_SIZE(key));
+}
+
 uc_vm_t *wpa_ucode_create_vm(void)
 {
 	static uc_parse_config_t config = {
diff --git a/package/network/services/hostapd/src/src/utils/ucode.h b/package/network/services/hostapd/src/src/utils/ucode.h
index c083241e07..a273c19b7b 100644
--- a/package/network/services/hostapd/src/src/utils/ucode.h
+++ b/package/network/services/hostapd/src/src/utils/ucode.h
@@ -25,6 +25,7 @@ uc_value_t *uc_wpa_udebug_set(uc_vm_t *vm, size_t nargs);
 uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs);
 uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs);
 uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs);
+uc_value_t *uc_wpa_rkh_derive_key(uc_vm_t *vm, size_t nargs);
 uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs);
 
 #endif




More information about the lede-commits mailing list