[RFC,1/2] AP: keep track and expose WPA-PSK PMK info of each station

Message ID 20181121214205.443-2-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>

This allows identifying which passphrase a station
used to connect.

When station connects a new event is emitted after
AP-STA-CONNECTED:

 wlan0: AP-STA-CONNECTED 00:36:76:21:dc:7b
 wlan0: AP-STA-CONNECTED-PMK 00:36:76:21:dc:7b 51ff78eb647fb7bafd9cd09a49dd264010d2cfe7b88ab8f107ecb06549e8f8d3

It's also possible to retrieve it through cli:

 $ hostapd_cli all_sta
 Selected interface 'ap0'
 00:36:76:21:dc:7b
 ...
 hostapdWPAPMK=51ff78eb647fb7bafd9cd09a49dd264010d2cfe7b88ab8f107ecb06549e8f8d3

The PMK can be already generated easily with
wpa_passphrase tool:

 $ wpa_passphrase ssid secretpassword
 network={
         ssid="ssid"
         #psk="secretpassword"
         psk=6ef4903f55512d167e4f1df7e16856563a99c7a4df299d6e877b3f5b9887c3dc
 }

This isn't interesting on its own but can be
useful, e.g. to firewall clients based on
passphrases.

Signed-off-by: Michal Kazior <michal@plume.com>
---
 src/ap/sta_info.c     | 28 ++++++++++++++++++++++++++++
 src/ap/wpa_auth.c     | 18 ++++++++++++++++++
 src/ap/wpa_auth.h     |  1 +
 src/ap/wpa_auth_ft.c  |  2 ++
 src/common/wpa_ctrl.h |  1 +
 5 files changed, 50 insertions(+)

Patch

diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 179cf43b6..99e045e3f 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -1165,6 +1165,33 @@  void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
 #endif /* CONFIG_IEEE80211W */
 
 
+static void ap_sta_wpa_msg_pmk(struct hostapd_data *hapd,
+			       struct sta_info *sta)
+{
+	const u8 *pmk;
+	char *hex;
+	int pmk_len;
+	int hex_len;
+
+	pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
+	if (!pmk)
+		return;
+	hex_len = pmk_len * 2 + 1;
+	hex = os_malloc(hex_len);
+	if (!hex)
+		return;
+
+	wpa_snprintf_hex(hex, hex_len, pmk, pmk_len);
+	wpa_msg((hapd->msg_ctx_parent &&
+		 hapd->msg_ctx_parent != hapd->msg_ctx)
+		? hapd->msg_ctx_parent
+		: hapd->msg_ctx,
+		MSG_INFO, AP_STA_CONNECTED_PWD MACSTR " %s",
+		MAC2STR(sta->addr), hex);
+	os_free(hex);
+}
+
+
 void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
 			   int authorized)
 {
@@ -1222,6 +1249,7 @@  void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
 			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
 					  AP_STA_CONNECTED "%s%s",
 					  buf, ip_addr);
+		ap_sta_wpa_msg_pmk(hapd, sta);
 	} else {
 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
 
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 34969e79e..bdef1edfe 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -860,6 +860,8 @@  static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
 
 		if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
 				       data, data_len) == 0) {
+			os_memcpy(sm->PMK, pmk, pmk_len);
+			sm->pmk_len = pmk_len;
 			ok = 1;
 			break;
 		}
@@ -2675,6 +2677,8 @@  SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
 		    wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
 				       sm->last_rx_eapol_key,
 				       sm->last_rx_eapol_key_len) == 0) {
+			os_memcpy(sm->PMK, pmk, pmk_len);
+			sm->pmk_len = pmk_len;
 			ok = 1;
 			break;
 		}
@@ -3940,6 +3944,13 @@  int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
 		return len;
 	len += ret;
 
+	if (sm->pmk_len) {
+		len += os_snprintf(buf + len, buflen - len, "hostapdWPAPMK=");
+		len += wpa_snprintf_hex(buf + len, buflen - len,
+					sm->PMK, sm->pmk_len);
+		len += os_snprintf(buf + len, buflen - len, "\n");
+	}
+
 	return len;
 }
 
@@ -3963,6 +3974,13 @@  int wpa_auth_get_pairwise(struct wpa_state_machine *sm)
 }
 
 
+const u8 *wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len)
+{
+	*len = sm->pmk_len;
+	return sm->PMK;
+}
+
+
 int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
 {
 	if (sm == NULL)
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index fad5536f7..7e82068e3 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -339,6 +339,7 @@  int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen);
 void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth);
 int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
 int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
+const u8 *wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len);
 int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
 int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
 int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index e8d46ab0d..391fe465a 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -2596,6 +2596,8 @@  static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
 		os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN);
 		if (out_pairwise)
 			*out_pairwise = pairwise;
+		os_memcpy(sm->PMK, pmk, PMK_LEN);
+		sm->pmk_len = PMK_LEN;
 		if (out_vlan &&
 		    wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) {
 			wpa_printf(MSG_DEBUG, "FT: vlan not available for STA "
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index f65077e04..73ad24402 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -297,6 +297,7 @@  extern "C" {
 #define WPS_EVENT_AP_PIN_ENABLED "WPS-AP-PIN-ENABLED "
 #define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED "
 #define AP_STA_CONNECTED "AP-STA-CONNECTED "
+#define AP_STA_CONNECTED_PWD "AP-STA-CONNECTED-PWD "
 #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
 #define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH "
 #define AP_STA_POLL_OK "AP-STA-POLL-OK "