wifi-scripts: wifi-station PSKs with a user's wpa_psk_file

iface_wpa_stations() wrote the wifi-station PSKs to
/var/run/hostapd-<ifname>.psk and named that file only where the user
had not set wpa_psk_file. With a user's file, hostapd read that file
alone, and the wifi-station PSKs never reached it, as it was for SAE
before the commit "wifi-scripts: wifi-station SAE passwords with a
user's file".

hostapd keeps one wpa_psk_file: config_file.c replaces the path at each
wpa_psk_file line, and hostapd_config_read_wpa_psk() reads lines of
"[keyid=..] [wps=..] [vlanid=..] <MAC> <PSK or passphrase>", skips
empty lines and lines that start with '#', and fails the setup of the
BSS when the file is missing. A second wpa_psk_file line is therefore
no option, and the user's lines are copied into the generated file.

Move the copy that iface_sae_stations() does into station_file_write()
and use it for both files: it writes the user's lines, then the
wifi-station lines, and names the generated file; a user's file that
cannot be read stays named, so hostapd reports it as before.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
Felix Fietkau committed 2026-10-08 09:50:39 +02:00
1 parent 79cb1d926f
commit 27608c1fc7
1 file changed
+40 -38
@@ -455,52 +455,54 @@ function iface_vlan(interface, config, vlans) {
]);
}
function iface_wpa_stations(config, stas) {
let path = `/var/run/hostapd-${config.ifname}.psk`;
/* hostapd uses only the last wpa_psk_file line, and the hostapd service
* hashes one file per option, so the user's file is copied */
function station_file_write(config, option, path, lines) {
const user_entries = config[option] ? fs.readfile(config[option]) : null;
let file = fs.open(path, 'w');
for (let k, sta in stas)
if (sta.config.mac && sta.config.key) {
for (let mac in sta.config.mac) {
let station = `${mac} ${sta.config.key}\n`;
if (sta.config.vid)
station = `vlanid=${sta.config.vid} ` + station;
file.write(station);
}
}
file.close();
set_default(config, 'wpa_psk_file', path);
}
/* the hostapd service hashes one file per option, so the user's file is
* copied */
function iface_sae_stations(config, stas) {
let path = `/var/run/hostapd-${config.ifname}.sae`;
let user_entries = config.sae_password_file ? fs.readfile(config.sae_password_file) : null;
if (config.sae_password_file && user_entries == null)
if (config[option] && user_entries == null)
return;
let file = fs.open(path, 'w');
if (length(user_entries))
file.write(rtrim(user_entries, '\n') + '\n');
for (let k, sta in stas)
if (sta.config.mac && sta.config.key) {
for (let mac in sta.config.mac) {
if (mac == '00:00:00:00:00:00')
mac = 'ff:ff:ff:ff:ff:ff';
let station = `${sta.config.key}|mac=${mac}`;
if (sta.config.vid)
station = station + `|vlanid=${sta.config.vid}`;
station = station + '\n';
file.write(station);
}
}
for (let line in lines)
file.write(line + '\n');
file.close();
config.sae_password_file = path;
config[option] = path;
}
function iface_wpa_stations(config, stas) {
let lines = [];
for (let k, sta in stas) {
if (!sta.config.mac || !sta.config.key)
continue;
for (let mac in sta.config.mac)
push(lines, (sta.config.vid ? `vlanid=${sta.config.vid} ` : '') + `${mac} ${sta.config.key}`);
}
station_file_write(config, 'wpa_psk_file', `/var/run/hostapd-${config.ifname}.psk`, lines);
}
function iface_sae_stations(config, stas) {
let lines = [];
for (let k, sta in stas) {
if (!sta.config.mac || !sta.config.key)
continue;
for (let mac in sta.config.mac) {
if (mac == '00:00:00:00:00:00')
mac = 'ff:ff:ff:ff:ff:ff';
push(lines, `${sta.config.key}|mac=${mac}` + (sta.config.vid ? `|vlanid=${sta.config.vid}` : ''));
}
}
station_file_write(config, 'sae_password_file', `/var/run/hostapd-${config.ifname}.sae`, lines);
}
function iface_eap_server(config) {