diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 33819ab8b..c4108e7f9 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -2307,6 +2307,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = hostapd_ctrl_iface_track_sta_list( hapd, reply, reply_size); #endif /* NEED_AP_MLME */ + } else if (os_strcmp(buf, "PMKSA") == 0) { + reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply, + reply_size); } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index fc8a72e38..1ee251e1b 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -1130,6 +1130,12 @@ static int hostapd_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int hostapd_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PMKSA"); +} + + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); @@ -1189,6 +1195,7 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { { "disable", hostapd_cli_cmd_disable }, { "erp_flush", hostapd_cli_cmd_erp_flush }, { "log_level", hostapd_cli_cmd_log_level }, + { "pmksa", hostapd_cli_cmd_pmksa }, { NULL, NULL } }; diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index 9f249d741..cfe25e5ce 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -568,3 +568,10 @@ int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd) { return hostapd_drv_stop_ap(hapd); } + + +int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, + size_t len) +{ + return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len); +} diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h index e5297d03e..43c9f7c7e 100644 --- a/src/ap/ctrl_iface_ap.h +++ b/src/ap/ctrl_iface_ap.h @@ -24,5 +24,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, int hostapd_parse_csa_settings(const char *pos, struct csa_settings *settings); int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd); +int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, + size_t len); #endif /* CTRL_IFACE_AP_H */ diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c index 30be30c29..8f1ca3e55 100644 --- a/src/ap/pmksa_cache_auth.c +++ b/src/ap/pmksa_cache_auth.c @@ -547,3 +547,48 @@ int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, return found ? 0 : -1; } + + +/** + * pmksa_cache_auth_list - Dump text list of entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @buf: Buffer for the list + * @len: Length of the buffer + * Returns: Number of bytes written to buffer + * + * This function is used to generate a text format representation of the + * current PMKSA cache contents for the ctrl_iface PMKSA command. + */ +int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) +{ + int i, ret; + char *pos = buf; + struct rsn_pmksa_cache_entry *entry; + struct os_reltime now; + + os_get_reltime(&now); + ret = os_snprintf(pos, buf + len - pos, + "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n"); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + i = 0; + entry = pmksa->pmksa; + while (entry) { + ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", + i, MAC2STR(entry->spa)); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, + PMKID_LEN); + ret = os_snprintf(pos, buf + len - pos, " %d %d\n", + (int) (entry->expiration - now.sec), + entry->opportunistic); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + entry = entry->next; + } + return pos - buf; +} diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h index 3e6027714..aa6f4cd77 100644 --- a/src/ap/pmksa_cache_auth.h +++ b/src/ap/pmksa_cache_auth.h @@ -63,5 +63,6 @@ void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry); int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, struct radius_das_attrs *attr); +int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); #endif /* PMKSA_CACHE_H */ diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 4ded9bb63..57839feea 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -3359,6 +3359,15 @@ void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, } +int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, + size_t len) +{ + if (!wpa_auth || !wpa_auth->pmksa) + return 0; + return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len); +} + + /* * Remove and free the group from wpa_authenticator. This is triggered by a * callback to make sure nobody is currently iterating the group list while it diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 2b601146c..0d099805c 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -298,6 +298,8 @@ int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *pmk, const u8 *pmkid); void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); +int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, + size_t len); int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack); diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index fd5b03fd1..716fa8a94 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -1366,6 +1366,44 @@ int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s) #endif /* CONFIG_CTRL_IFACE */ +int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, + size_t len) +{ + size_t reply_len = 0, i; + char ap_delimiter[] = "---- AP ----\n"; + char mesh_delimiter[] = "---- mesh ----\n"; + size_t dlen; + + if (wpa_s->ap_iface) { + dlen = os_strlen(ap_delimiter); + if (dlen > len - reply_len) + return reply_len; + os_memcpy(&buf[reply_len], ap_delimiter, dlen); + reply_len += dlen; + + for (i = 0; i < wpa_s->ap_iface->num_bss; i++) { + reply_len += hostapd_ctrl_iface_pmksa_list( + wpa_s->ap_iface->bss[i], + &buf[reply_len], len - reply_len); + } + } + + if (wpa_s->ifmsh) { + dlen = os_strlen(mesh_delimiter); + if (dlen > len - reply_len) + return reply_len; + os_memcpy(&buf[reply_len], mesh_delimiter, dlen); + reply_len += dlen; + + reply_len += hostapd_ctrl_iface_pmksa_list( + wpa_s->ifmsh->bss[0], &buf[reply_len], + len - reply_len); + } + + return reply_len; +} + + #ifdef NEED_AP_MLME void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s, struct dfs_event *radar) diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h index 2b8a1d42c..c3c1d9f5f 100644 --- a/wpa_supplicant/ap.h +++ b/wpa_supplicant/ap.h @@ -82,6 +82,9 @@ int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s); +int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, + size_t len); + void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s, struct dfs_event *radar); void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s, diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 36fc4b44d..98f7e29ae 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -8354,6 +8354,20 @@ static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, } +static int wpas_ctrl_iface_pmksa(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + size_t reply_len; + + reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, buf, buflen); +#ifdef CONFIG_AP + reply_len += wpas_ap_pmksa_cache_list(wpa_s, &buf[reply_len], + buflen - reply_len); +#endif /* CONFIG_AP */ + return reply_len; +} + + static int wpas_ctrl_cmd_debug_level(const char *cmd) { if (os_strcmp(cmd, "PING") == 0 || @@ -8425,8 +8439,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = wpa_supplicant_ctrl_iface_status( wpa_s, buf + 6, reply, reply_size); } else if (os_strcmp(buf, "PMKSA") == 0) { - reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, reply, - reply_size); + reply_len = wpas_ctrl_iface_pmksa(wpa_s, reply, reply_size); } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); } else if (os_strncmp(buf, "SET ", 4) == 0) {