[RFC,2/2] AP: add wpa_psk_file reloading in runtime

Message ID 20181121214205.443-3-kazikcz@gmail.com
State New
Headers show
Series
  • AP: make wpa_psk_file more dynamic
Related show

Commit Message

Michal Kazior Nov. 21, 2018, 9:42 p.m.
From: Michal Kazior <michal@plume.com>

With the change 'AP: keep track and expose WPA-PSK
PMK of each station' it is now possible to tell
whether given station is allowed to be connected
with password that it used at any point in time.

The wpa_psk_file can now be modified and hostapd
can be told to re-read it with:

 $ hostapd_cli reload_wpa_psk

Stations that used a passphrases that is no longer
in the wpa_psk_file will be disconnected.

It must be noted special care must be taken if WPS
is configured (wps_state=2, eap_server=1) because
WPS appends PMKs to the wpa_psk_file.

Signed-off-by: Michal Kazior <michal@plume.com>
---
 hostapd/ctrl_iface.c  | 61 +++++++++++++++++++++++++++++++++++++++++++
 hostapd/hostapd_cli.c |  9 +++++++
 2 files changed, 70 insertions(+)

Patch

diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index e539a0902..ca2866232 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -1488,6 +1488,64 @@  static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface)
 }
 
 
+static int hostapd_ctrl_iface_kick_mismatch_psk_sta_iter(struct hostapd_data *hapd,
+							 struct sta_info *sta,
+							 void *ctx)
+{
+	struct hostapd_wpa_psk *psk;
+	const u8 *pmk;
+	int pmk_len;
+	int pmk_match;
+	int sta_match;
+	int bss_match;
+	int reason;
+
+	pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
+
+	for (psk = hapd->conf->ssid.wpa_psk; pmk && psk; psk = psk->next) {
+		pmk_match = PMK_LEN == pmk_len && !os_memcmp(psk->psk, pmk, pmk_len);
+		sta_match = psk->group == 0 && !os_memcmp(sta->addr, psk->addr, ETH_ALEN);
+		bss_match = psk->group == 1;
+
+		if (pmk_match && (sta_match || bss_match))
+			return 0;
+	}
+
+	wpa_printf(MSG_INFO, "STA " MACSTR " password no longer valid, kicking", MAC2STR(sta->addr));
+	reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
+	hostapd_drv_sta_deauth(hapd, sta->addr, reason);
+	ap_sta_deauthenticate(hapd, sta, reason);
+
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_reload_wpa_psk(struct hostapd_data *hapd)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+	struct hostapd_wpa_psk *psk, *next;
+	int err;
+
+	for (psk = conf->ssid.wpa_psk; psk; psk = next) {
+		next = psk->next;
+		os_free(psk);
+	}
+	conf->ssid.wpa_psk = NULL;
+
+	err = hostapd_setup_wpa_psk(conf);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "Reloading WPA-PSK passwords failed: %d", err);
+		return -1;
+	}
+
+	ap_for_each_sta(hapd,
+			hostapd_ctrl_iface_kick_mismatch_psk_sta_iter,
+			NULL);
+
+	return 0;
+}
+
+
 #ifdef CONFIG_TESTING_OPTIONS
 
 static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd)
@@ -3013,6 +3071,9 @@  static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
 	} else if (os_strncmp(buf, "ENABLE", 6) == 0) {
 		if (hostapd_ctrl_iface_enable(hapd->iface))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "RELOAD_WPA_PSK", 14) == 0) {
+		if (hostapd_ctrl_iface_reload_wpa_psk(hapd))
+			reply_len = -1;
 	} else if (os_strncmp(buf, "RELOAD", 6) == 0) {
 		if (hostapd_ctrl_iface_reload(hapd->iface))
 			reply_len = -1;
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index fbec5d246..7e9cee1bb 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -903,6 +903,13 @@  static int hostapd_cli_cmd_hs20_deauth_req(struct wpa_ctrl *ctrl, int argc,
 }
 
 
+static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "RELOAD_WPA_PSK");
+}
+
+
 static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	hostapd_cli_quit = 1;
@@ -1651,6 +1658,8 @@  static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
 	  "=Add/Delete/Show/Clear deny MAC ACL" },
 	{ "poll_sta", hostapd_cli_cmd_poll_sta, hostapd_complete_stations,
 	  "<addr> = poll a STA to check connectivity with a QoS null frame" },
+	{ "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL,
+	  "=reload wpa_psk_file only" },
 	{ NULL, NULL, NULL, NULL }
 };