External persistent storage for PMKSA cache entries
This adds new wpa_supplicant control interface commands PMKSA_GET and PMKSA_ADD that can be used to store PMKSA cache entries in an external persistent storage when terminating a wpa_supplicant process and then restore those entries when starting a new process. The previously added PMKSA-CACHE-ADDED/REMOVED events can be used to help in synchronizing the external storage with the memory-only volatile storage within wpa_supplicant. "PMKSA_GET <network_id>" fetches all stored PMKSA cache entries bound to a specific network profile. The network_id of the current profile is available with the STATUS command (id=<network_id). In addition, the network_id is included in the PMKSA-CACHE-ADDED/REMOVED events. The output of the PMKSA_GET command uses the following format: <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> For example: 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30240 43200 1 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30240 43200 1 0 The PMKSA_GET command uses the following format: <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> (i.e., "PMKSA_ADD <network_id> " prefix followed by a line of PMKSA_GET output data; however, the reauth_time and expiration values need to be updated by decrementing them by number of seconds between the PMKSA_GET and PMKSA_ADD commands) For example: PMKSA_ADD 0 02:00:00:00:03:00 113b8b5dc8eda16594e8274df4caa3d4 355e98681d09e0b69d3a342f96998aa765d10c4459ac592459b5efc6b563eff6 30140 43100 1 0 PMKSA_ADD 0 02:00:00:00:04:00 bbdac8607aaaac28e16aacc9152ffe23 e3dd6adc390e685985e5f40e6fe72df846a0acadc59ba15c208d9cb41732a663 30140 43100 1 0 This functionality is disabled be default and can be enabled with CONFIG_PMKSA_CACHE_EXTERNAL=y build configuration option. It should be noted that this allows any process that has access to the wpa_supplicant control interface to use PMKSA_ADD command to fetch keying material (PMK), so this is for environments in which the control interface access is restricted. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
parent
f0df5afa08
commit
3459381dd2
9 changed files with 222 additions and 8 deletions
|
@ -129,7 +129,7 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
|
|||
const u8 *pmkid, const u8 *kck, size_t kck_len,
|
||||
const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
|
||||
{
|
||||
struct rsn_pmksa_cache_entry *entry, *pos, *prev;
|
||||
struct rsn_pmksa_cache_entry *entry;
|
||||
struct os_reltime now;
|
||||
|
||||
if (pmk_len > PMK_LEN_MAX)
|
||||
|
@ -160,14 +160,25 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
|
|||
os_memcpy(entry->aa, aa, ETH_ALEN);
|
||||
entry->network_ctx = network_ctx;
|
||||
|
||||
return pmksa_cache_add_entry(pmksa, entry);
|
||||
}
|
||||
|
||||
|
||||
struct rsn_pmksa_cache_entry *
|
||||
pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
|
||||
struct rsn_pmksa_cache_entry *entry)
|
||||
{
|
||||
struct rsn_pmksa_cache_entry *pos, *prev;
|
||||
|
||||
/* Replace an old entry for the same Authenticator (if found) with the
|
||||
* new entry */
|
||||
pos = pmksa->pmksa;
|
||||
prev = NULL;
|
||||
while (pos) {
|
||||
if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
|
||||
if (pos->pmk_len == pmk_len &&
|
||||
os_memcmp_const(pos->pmk, pmk, pmk_len) == 0 &&
|
||||
if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0) {
|
||||
if (pos->pmk_len == entry->pmk_len &&
|
||||
os_memcmp_const(pos->pmk, entry->pmk,
|
||||
entry->pmk_len) == 0 &&
|
||||
os_memcmp_const(pos->pmkid, entry->pmkid,
|
||||
PMKID_LEN) == 0) {
|
||||
wpa_printf(MSG_DEBUG, "WPA: reusing previous "
|
||||
|
@ -193,8 +204,8 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
|
|||
"the current AP and any PMKSA cache entry "
|
||||
"that was based on the old PMK");
|
||||
if (!pos->opportunistic)
|
||||
pmksa_cache_flush(pmksa, network_ctx, pos->pmk,
|
||||
pos->pmk_len);
|
||||
pmksa_cache_flush(pmksa, entry->network_ctx,
|
||||
pos->pmk, pos->pmk_len);
|
||||
pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE);
|
||||
break;
|
||||
}
|
||||
|
@ -245,8 +256,9 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
|
|||
}
|
||||
pmksa->pmksa_count++;
|
||||
wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
|
||||
" network_ctx=%p", MAC2STR(entry->aa), network_ctx);
|
||||
wpa_sm_add_pmkid(pmksa->sm, network_ctx, entry->aa, entry->pmkid);
|
||||
" network_ctx=%p", MAC2STR(entry->aa), entry->network_ctx);
|
||||
wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
|
||||
entry->pmkid);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
@ -514,6 +526,12 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
|
|||
}
|
||||
|
||||
|
||||
struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa)
|
||||
{
|
||||
return pmksa->pmksa;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pmksa_cache_init - Initialize PMKSA cache
|
||||
* @free_cb: Callback function to be called when a PMKSA cache entry is freed
|
||||
|
|
|
@ -55,10 +55,14 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
|
|||
const u8 *aa, const u8 *pmkid,
|
||||
const void *network_ctx);
|
||||
int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
|
||||
struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa);
|
||||
struct rsn_pmksa_cache_entry *
|
||||
pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
|
||||
const u8 *pmkid, const u8 *kck, size_t kck_len,
|
||||
const u8 *aa, const u8 *spa, void *network_ctx, int akmp);
|
||||
struct rsn_pmksa_cache_entry *
|
||||
pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
|
||||
struct rsn_pmksa_cache_entry *entry);
|
||||
struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm);
|
||||
void pmksa_cache_clear_current(struct wpa_sm *sm);
|
||||
int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
|
||||
|
|
|
@ -3028,6 +3028,20 @@ int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len)
|
|||
}
|
||||
|
||||
|
||||
struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_head(struct wpa_sm *sm)
|
||||
{
|
||||
return pmksa_cache_head(sm->pmksa);
|
||||
}
|
||||
|
||||
|
||||
struct rsn_pmksa_cache_entry *
|
||||
wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm,
|
||||
struct rsn_pmksa_cache_entry * entry)
|
||||
{
|
||||
return pmksa_cache_add_entry(sm->pmksa, entry);
|
||||
}
|
||||
|
||||
|
||||
void wpa_sm_drop_sa(struct wpa_sm *sm)
|
||||
{
|
||||
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
|
||||
|
|
|
@ -149,6 +149,10 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
|
|||
const u8 *buf, size_t len);
|
||||
int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data);
|
||||
int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len);
|
||||
struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_head(struct wpa_sm *sm);
|
||||
struct rsn_pmksa_cache_entry *
|
||||
wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm,
|
||||
struct rsn_pmksa_cache_entry * entry);
|
||||
void wpa_sm_drop_sa(struct wpa_sm *sm);
|
||||
int wpa_sm_has_ptk(struct wpa_sm *sm);
|
||||
|
||||
|
|
|
@ -270,6 +270,10 @@ ifdef CONFIG_PEERKEY
|
|||
L_CFLAGS += -DCONFIG_PEERKEY
|
||||
endif
|
||||
|
||||
ifdef CONFIG_PMKSA_CACHE_EXTERNAL
|
||||
L_CFLAGS += -DCONFIG_PMKSA_CACHE_EXTERNAL
|
||||
endif
|
||||
|
||||
ifndef CONFIG_NO_WPA
|
||||
OBJS += src/rsn_supp/wpa.c
|
||||
OBJS += src/rsn_supp/preauth.c
|
||||
|
|
|
@ -303,6 +303,10 @@ ifdef CONFIG_PEERKEY
|
|||
CFLAGS += -DCONFIG_PEERKEY
|
||||
endif
|
||||
|
||||
ifdef CONFIG_PMKSA_CACHE_EXTERNAL
|
||||
CFLAGS += -DCONFIG_PMKSA_CACHE_EXTERNAL
|
||||
endif
|
||||
|
||||
ifndef CONFIG_NO_WPA
|
||||
OBJS += ../src/rsn_supp/wpa.o
|
||||
OBJS += ../src/rsn_supp/preauth.o
|
||||
|
|
|
@ -489,4 +489,9 @@ CONFIG_WIFI_DISPLAY=y
|
|||
# Support Multi Band Operation
|
||||
#CONFIG_MBO=y
|
||||
|
||||
# External PMKSA cache control
|
||||
# This can be used to enable control interface commands that allow the current
|
||||
# PMKSA cache entries to be fetched and new entries to be added.
|
||||
#CONFIG_PMKSA_CACHE_EXTERNAL=y
|
||||
|
||||
include $(wildcard $(LOCAL_PATH)/android_config_*.inc)
|
||||
|
|
|
@ -8714,6 +8714,154 @@ static void wpas_ctrl_iface_pmksa_flush(struct wpa_supplicant *wpa_s)
|
|||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
|
||||
|
||||
static int wpas_ctrl_iface_pmksa_get(struct wpa_supplicant *wpa_s,
|
||||
const char *cmd, char *buf, size_t buflen)
|
||||
{
|
||||
struct rsn_pmksa_cache_entry *entry;
|
||||
struct wpa_ssid *ssid;
|
||||
char *pos, *pos2, *end;
|
||||
int ret;
|
||||
struct os_reltime now;
|
||||
|
||||
ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd));
|
||||
if (!ssid)
|
||||
return -1;
|
||||
|
||||
pos = buf;
|
||||
end = buf + buflen;
|
||||
|
||||
os_get_reltime(&now);
|
||||
|
||||
/*
|
||||
* Entry format:
|
||||
* <BSSID> <PMKID> <PMK> <reauth_time in seconds>
|
||||
* <expiration in seconds> <akmp> <opportunistic>
|
||||
*/
|
||||
|
||||
for (entry = wpa_sm_pmksa_cache_head(wpa_s->wpa); entry;
|
||||
entry = entry->next) {
|
||||
if (entry->network_ctx != ssid)
|
||||
continue;
|
||||
|
||||
pos2 = pos;
|
||||
ret = os_snprintf(pos2, end - pos2, MACSTR " ",
|
||||
MAC2STR(entry->aa));
|
||||
if (os_snprintf_error(end - pos2, ret))
|
||||
break;
|
||||
pos2 += ret;
|
||||
|
||||
pos2 += wpa_snprintf_hex(pos2, end - pos2, entry->pmkid,
|
||||
PMKID_LEN);
|
||||
|
||||
ret = os_snprintf(pos2, end - pos2, " ");
|
||||
if (os_snprintf_error(end - pos2, ret))
|
||||
break;
|
||||
pos2 += ret;
|
||||
|
||||
pos2 += wpa_snprintf_hex(pos2, end - pos2, entry->pmk,
|
||||
entry->pmk_len);
|
||||
|
||||
ret = os_snprintf(pos2, end - pos2, " %d %d %d %d",
|
||||
(int) (entry->reauth_time - now.sec),
|
||||
(int) (entry->expiration - now.sec),
|
||||
entry->akmp,
|
||||
entry->opportunistic);
|
||||
if (os_snprintf_error(end - pos2, ret))
|
||||
break;
|
||||
pos2 += ret;
|
||||
|
||||
ret = os_snprintf(pos2, end - pos2, "\n");
|
||||
if (os_snprintf_error(end - pos2, ret))
|
||||
break;
|
||||
pos2 += ret;
|
||||
|
||||
pos = pos2;
|
||||
}
|
||||
|
||||
return pos - buf;
|
||||
}
|
||||
|
||||
|
||||
static int wpas_ctrl_iface_pmksa_add(struct wpa_supplicant *wpa_s,
|
||||
char *cmd)
|
||||
{
|
||||
struct rsn_pmksa_cache_entry *entry;
|
||||
struct wpa_ssid *ssid;
|
||||
char *pos, *pos2;
|
||||
int ret = -1;
|
||||
struct os_reltime now;
|
||||
int reauth_time = 0, expiration = 0;
|
||||
|
||||
/*
|
||||
* Entry format:
|
||||
* <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds>
|
||||
* <expiration in seconds> <akmp> <opportunistic>
|
||||
*/
|
||||
|
||||
ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd));
|
||||
if (!ssid)
|
||||
return -1;
|
||||
|
||||
pos = os_strchr(cmd, ' ');
|
||||
if (!pos)
|
||||
return -1;
|
||||
pos++;
|
||||
|
||||
entry = os_zalloc(sizeof(*entry));
|
||||
if (!entry)
|
||||
return -1;
|
||||
|
||||
if (hwaddr_aton(pos, entry->aa))
|
||||
goto fail;
|
||||
|
||||
pos = os_strchr(pos, ' ');
|
||||
if (!pos)
|
||||
goto fail;
|
||||
pos++;
|
||||
|
||||
if (hexstr2bin(pos, entry->pmkid, PMKID_LEN) < 0)
|
||||
goto fail;
|
||||
|
||||
pos = os_strchr(pos, ' ');
|
||||
if (!pos)
|
||||
goto fail;
|
||||
pos++;
|
||||
|
||||
pos2 = os_strchr(pos, ' ');
|
||||
if (!pos2)
|
||||
goto fail;
|
||||
entry->pmk_len = (pos2 - pos) / 2;
|
||||
if (entry->pmk_len < PMK_LEN || entry->pmk_len > PMK_LEN_MAX ||
|
||||
hexstr2bin(pos, entry->pmk, entry->pmk_len) < 0)
|
||||
goto fail;
|
||||
|
||||
pos = os_strchr(pos, ' ');
|
||||
if (!pos)
|
||||
goto fail;
|
||||
pos++;
|
||||
|
||||
if (sscanf(pos, "%d %d %d %d", &reauth_time, &expiration,
|
||||
&entry->akmp, &entry->opportunistic) != 4)
|
||||
goto fail;
|
||||
os_get_reltime(&now);
|
||||
entry->expiration = now.sec + expiration;
|
||||
entry->reauth_time = now.sec + reauth_time;
|
||||
|
||||
entry->network_ctx = ssid;
|
||||
|
||||
wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
|
||||
entry = NULL;
|
||||
ret = 0;
|
||||
fail:
|
||||
os_free(entry);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
|
||||
|
||||
|
||||
static int wpas_ctrl_cmd_debug_level(const char *cmd)
|
||||
{
|
||||
if (os_strcmp(cmd, "PING") == 0 ||
|
||||
|
@ -8788,6 +8936,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
|
|||
reply_len = wpas_ctrl_iface_pmksa(wpa_s, reply, reply_size);
|
||||
} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
|
||||
wpas_ctrl_iface_pmksa_flush(wpa_s);
|
||||
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
|
||||
} else if (os_strncmp(buf, "PMKSA_GET ", 10) == 0) {
|
||||
reply_len = wpas_ctrl_iface_pmksa_get(wpa_s, buf + 10,
|
||||
reply, reply_size);
|
||||
} else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) {
|
||||
if (wpas_ctrl_iface_pmksa_add(wpa_s, buf + 10) < 0)
|
||||
reply_len = -1;
|
||||
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
|
||||
} else if (os_strncmp(buf, "SET ", 4) == 0) {
|
||||
if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
|
||||
reply_len = -1;
|
||||
|
|
|
@ -562,3 +562,8 @@ CONFIG_PEERKEY=y
|
|||
# This is needed to be able to use mode=1 network profile with proto=RSN and
|
||||
# key_mgmt=WPA-PSK (i.e., full key management instead of WPA-None).
|
||||
#CONFIG_IBSS_RSN=y
|
||||
|
||||
# External PMKSA cache control
|
||||
# This can be used to enable control interface commands that allow the current
|
||||
# PMKSA cache entries to be fetched and new entries to be added.
|
||||
#CONFIG_PMKSA_CACHE_EXTERNAL=y
|
||||
|
|
Loading…
Reference in a new issue