]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
External persistent storage for PMKSA cache entries
authorJouni Malinen <jouni@qca.qualcomm.com>
Mon, 12 Dec 2016 21:47:04 +0000 (23:47 +0200)
committerJouni Malinen <j@w1.fi>
Mon, 12 Dec 2016 21:47:04 +0000 (23:47 +0200)
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>
src/rsn_supp/pmksa_cache.c
src/rsn_supp/pmksa_cache.h
src/rsn_supp/wpa.c
src/rsn_supp/wpa.h
wpa_supplicant/Android.mk
wpa_supplicant/Makefile
wpa_supplicant/android.config
wpa_supplicant/ctrl_iface.c
wpa_supplicant/defconfig

index 804aba380f9130ffe28b437792e77f1eb48770e7..f723bb0a3a58b731db633137299df6aff8068175 100644 (file)
@@ -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
index daede6dac7fe6abfbb9204d3e2ecd0e1601f6549..8237e63a95b043fdd5d6eecd25038a6dc142357e 100644 (file)
@@ -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,
index 65e257a939aceafc67b032c85f4cd1dd9610f81c..80936346f0a39a94f34f5086f1f3c0b2d37510bb 100644 (file)
@@ -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");
index 8ccc7da7dce8faa8697b67b41dc1a6ed9376783c..d1a810bea00897b9670bccdf93f7941342978a39 100644 (file)
@@ -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);
 
index 46c99d61f6262ebdd35f16069f00a34c08c8be9e..e7b745787a7468a3a0c7b2dfce88a2c65365e946 100644 (file)
@@ -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
index 41c61c1097e7ae8fe3fdae8539fd91d8cd6884cf..e9e83be5e4cc365376b0c6b24cddb977ab5c6bca 100644 (file)
@@ -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
index 02505bb991aa4f0fb0af72bc28f644173780113d..e167f9fddafcead2842505c0d76ff29ce3f46b8e 100644 (file)
@@ -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)
index 05d1f7b8c9f7587d5e4d3244d647197730fde941..beb06ed5f0e3469753079aeed24a4e9a2eb2875d 100644 (file)
@@ -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;
index aca9e8199ae94092499244202538a903a9ca2078..f6df81a24c1ad15e60c847d92c49ec3740a9b58f 100644 (file)
@@ -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