]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
mesh: Add support for PMKSA caching
authorMasashi Honma <masashi.honma@gmail.com>
Wed, 9 Mar 2016 09:16:14 +0000 (18:16 +0900)
committerJouni Malinen <j@w1.fi>
Sun, 20 Mar 2016 15:56:38 +0000 (17:56 +0200)
This patch add functionality of mesh SAE PMKSA caching. If the local STA
already has peer's PMKSA entry in the cache, skip SAE authentication and
start AMPE with the cached value.

If the peer does not support PMKSA caching or does not have the local
STA's PMKSA entry in the cache, AMPE will fail and the PMKSA cache entry
of the peer will be removed. Then STA retries with ordinary SAE
authentication.

If the peer does not support PMKSA caching and the local STA uses
no_auto_peer=1, the local STA can not retry SAE authentication because
NEW_PEER_CANDIDATE event cannot start SAE authentication when
no_auto_peer=1. So this patch extends MESH_PEER_ADD command to use
duration(sec). Throughout the duration, the local STA can start SAE
authentication triggered by NEW_PEER_CANDIDATE even though
no_auto_peer=1.

This commit requires commit 70c93963edefa37ef84b73efb9d04ea10268341c
('SAE: Fix PMKID calculation for PMKSA cache'). Without that commit,
chosen PMK comparison will fail.

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
13 files changed:
src/ap/hostapd.h
src/ap/ieee802_11.c
src/ap/ieee802_11.h
src/ap/sta_info.h
src/ap/wpa_auth.c
src/ap/wpa_auth.h
wpa_supplicant/ctrl_iface.c
wpa_supplicant/mesh.c
wpa_supplicant/mesh.h
wpa_supplicant/mesh_mpm.c
wpa_supplicant/mesh_mpm.h
wpa_supplicant/mesh_rsn.c
wpa_supplicant/wpa_cli.c

index 60516aecb1eb917e05d4e8ad64ce84c549643b55..75a7c04997723cb71f935afa55e6d14b4651e097 100644 (file)
@@ -262,6 +262,7 @@ struct hostapd_data {
                                 struct sta_info *sta);
        struct wpabuf *mesh_pending_auth;
        struct os_reltime mesh_pending_auth_time;
+       u8 mesh_required_peer[ETH_ALEN];
 #endif /* CONFIG_MESH */
 
 #ifdef CONFIG_SQLITE
index 134ae0620f4353c02f5f0908c8fd3f5084bd765b..6a373c54c25923247c9a2f6b4f02b5960d77b300 100644 (file)
@@ -554,6 +554,18 @@ static void sae_set_retransmit_timer(struct hostapd_data *hapd,
 }
 
 
+void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+       sta->flags |= WLAN_STA_AUTH;
+       sta->auth_alg = WLAN_AUTH_SAE;
+       mlme_authenticate_indication(hapd, sta);
+       wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+       sta->sae->state = SAE_ACCEPTED;
+       wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
+                              sta->sae->pmk, sta->sae->pmkid);
+}
+
+
 static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
                       const u8 *bssid, u8 auth_transaction)
 {
@@ -676,13 +688,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
 
                        sae_set_retransmit_timer(hapd, sta);
                } else {
-                       sta->flags |= WLAN_STA_AUTH;
-                       sta->auth_alg = WLAN_AUTH_SAE;
-                       mlme_authenticate_indication(hapd, sta);
-                       wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
-                       sta->sae->state = SAE_ACCEPTED;
-                       wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
-                                              sta->sae->pmk, sta->sae->pmkid);
+                       sae_accept_sta(hapd, sta);
                }
                break;
        case SAE_ACCEPTED:
@@ -691,6 +697,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
                                   ") doing reauthentication",
                                   MAC2STR(sta->addr));
                        ap_free_sta(hapd, sta);
+                       wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
                } else {
                        if (sae_check_big_sync(sta))
                                return WLAN_STATUS_SUCCESS;
@@ -733,6 +740,13 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
                sta->sae->sync = 0;
        }
 
+       if (sta->mesh_sae_pmksa_caching) {
+               wpa_printf(MSG_DEBUG,
+                          "SAE: Cancel use of mesh PMKSA caching because peer starts SAE authentication");
+               wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+               sta->mesh_sae_pmksa_caching = 0;
+       }
+
        if (auth_transaction == 1) {
                const u8 *token = NULL, *pos, *end;
                size_t token_len = 0;
index 1a96b33171c87b7ebaba8b1c635692d63f989cdc..71b3b497ad12c2ecbb627c201f737de4d9d62682 100644 (file)
@@ -102,6 +102,7 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
 #ifdef CONFIG_SAE
 void sae_clear_retransmit_timer(struct hostapd_data *hapd,
                                struct sta_info *sta);
+void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta);
 #else /* CONFIG_SAE */
 static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd,
                                              struct sta_info *sta)
index 70a59ccd797b898c04404bdd7036d0f40d39fbce..3d9a928aa803e036a9c07b516bdb07151299b970 100644 (file)
@@ -183,6 +183,7 @@ struct sta_info {
 
 #ifdef CONFIG_SAE
        struct sae_data *sae;
+       unsigned int mesh_sae_pmksa_caching:1;
 #endif /* CONFIG_SAE */
 
        u32 session_timeout; /* valid only if session_timeout_set == 1 */
index ad1a7bcc859970955349b6fd451acd9ecfdb3bff..779a40dbaa44b9fd553cb62c896aacd1448695f8 100644 (file)
@@ -3375,6 +3375,34 @@ void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth)
 }
 
 
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+{
+       if (!wpa_auth || !wpa_auth->pmksa)
+               return NULL;
+       return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
+}
+
+
+void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
+                             struct wpa_state_machine *sm,
+                             struct wpa_authenticator *wpa_auth,
+                             u8 *pmkid, u8 *pmk)
+{
+       if (!sm)
+               return;
+
+       sm->pmksa = pmksa;
+       if (sm->pmksa->pmk)
+               os_memcpy(pmk, sm->pmksa->pmk, PMK_LEN);
+       if (sm->pmksa->pmkid) {
+               os_memcpy(pmkid, sm->pmksa->pmkid, PMKID_LEN);
+               os_memcpy(wpa_auth->dot11RSNAPMKIDUsed,
+                         sm->pmksa->pmkid, PMKID_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
index 166976648546a372fde30dc74dd4c20c85195ce0..0de8d976e3f9240ea6de273ccb3ef80f00d7cab5 100644 (file)
@@ -301,6 +301,12 @@ 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);
 void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr);
+void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
+                             struct wpa_state_machine *sm,
+                             struct wpa_authenticator *wpa_auth,
+                             u8 *pmkid, u8 *pmk);
 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);
index 381d5210cb9c52fb76c2ae74e5e969e54e755991..a79200b54e4217afcf4cbab5b0562bd9974d9eaa 100644 (file)
@@ -2744,11 +2744,21 @@ static int wpa_supplicant_ctrl_iface_mesh_peer_add(
        struct wpa_supplicant *wpa_s, char *cmd)
 {
        u8 addr[ETH_ALEN];
+       int duration;
+       char *pos;
+
+       pos = os_strstr(cmd, " duration=");
+       if (pos) {
+               *pos = '\0';
+               duration = atoi(pos + 10);
+       } else {
+               duration = -1;
+       }
 
        if (hwaddr_aton(cmd, addr))
                return -1;
 
-       return wpas_mesh_peer_add(wpa_s, addr);
+       return wpas_mesh_peer_add(wpa_s, addr, duration);
 }
 
 #endif /* CONFIG_MESH */
index cdb5eb0be0465295e69c145344b456b2fc4a8daa..89b033bc8d15576ca513731ad9c95d912c847154 100644 (file)
@@ -605,7 +605,8 @@ int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, const u8 *addr)
 }
 
 
-int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr)
+int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr,
+                      int duration)
 {
-       return mesh_mpm_connect_peer(wpa_s, addr);
+       return mesh_mpm_connect_peer(wpa_s, addr, duration);
 }
index b40ae6c320d3c43ba441b5ac340e26fc68a9f43a..7317083c99cd866dbb1aad69d1494bbcc3399cf2 100644 (file)
@@ -19,7 +19,8 @@ int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
 int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname,
                            size_t len);
 int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, const u8 *addr);
-int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr);
+int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr,
+                      int duration);
 
 #ifdef CONFIG_MESH
 
index c470d1046f1bc54732071fc84a7428680cbac232..c014eaf86f6e8ae886a2f28936830c6e611a521a 100644 (file)
@@ -14,6 +14,7 @@
 #include "ap/hostapd.h"
 #include "ap/sta_info.h"
 #include "ap/ieee802_11.h"
+#include "ap/wpa_auth.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "mesh_mpm.h"
@@ -419,6 +420,7 @@ static void plink_timer(void *eloop_ctx, void *user_data)
        struct sta_info *sta = user_data;
        u16 reason = 0;
        struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+       struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
 
        switch (sta->plink_state) {
        case PLINK_OPEN_RCVD:
@@ -448,6 +450,13 @@ static void plink_timer(void *eloop_ctx, void *user_data)
                break;
        case PLINK_HOLDING:
                /* holding timer */
+
+               if (sta->mesh_sae_pmksa_caching) {
+                       wpa_printf(MSG_DEBUG, "MPM: Peer " MACSTR
+                                  " looks like it does not support mesh SAE PMKSA caching, so remove the cached entry for it",
+                                  MAC2STR(sta->addr));
+                       wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+               }
                mesh_mpm_fsm_restart(wpa_s, sta);
                break;
        default:
@@ -512,7 +521,17 @@ int mesh_mpm_close_peer(struct wpa_supplicant *wpa_s, const u8 *addr)
 }
 
 
-int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr)
+static void peer_add_timer(void *eloop_ctx, void *user_data)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+
+       os_memset(hapd->mesh_required_peer, 0, ETH_ALEN);
+}
+
+
+int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+                         int duration)
 {
        struct wpa_ssid *ssid = wpa_s->current_ssid;
        struct hostapd_data *hapd;
@@ -547,10 +566,14 @@ int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr)
                return -1;
        }
 
-       if (conf->security == MESH_CONF_SEC_NONE)
+       if (conf->security == MESH_CONF_SEC_NONE) {
                mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT);
-       else
+       } else {
                mesh_rsn_auth_sae_sta(wpa_s, sta);
+               os_memcpy(hapd->mesh_required_peer, addr, ETH_ALEN);
+               eloop_register_timeout(duration == -1 ? 10 : duration, 0,
+                                      peer_add_timer, wpa_s, NULL);
+       }
 
        return 0;
 }
@@ -565,6 +588,7 @@ void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh)
 
        hapd->num_plinks = 0;
        hostapd_free_stas(hapd);
+       eloop_cancel_timeout(peer_add_timer, wpa_s, NULL);
 }
 
 
@@ -705,7 +729,9 @@ void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
        if (!sta)
                return;
 
-       if (ssid && ssid->no_auto_peer) {
+       if (ssid && ssid->no_auto_peer &&
+           (is_zero_ether_addr(data->mesh_required_peer) ||
+            os_memcmp(data->mesh_required_peer, addr, ETH_ALEN) != 0)) {
                wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with "
                        MACSTR " because of no_auto_peer", MAC2STR(addr));
                if (data->mesh_pending_auth) {
@@ -785,7 +811,10 @@ static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s,
        hapd->num_plinks++;
 
        sta->flags |= WLAN_STA_ASSOC;
+       sta->mesh_sae_pmksa_caching = 0;
 
+       eloop_cancel_timeout(peer_add_timer, wpa_s, NULL);
+       peer_add_timer(wpa_s, NULL);
        eloop_cancel_timeout(plink_timer, wpa_s, sta);
 
        /* Send ctrl event */
@@ -1065,7 +1094,8 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s,
         * open mesh, then go ahead and add the peer before proceeding.
         */
        if (!sta && action_field == PLINK_OPEN &&
-           !(mconf->security & MESH_CONF_SEC_AMPE))
+           (!(mconf->security & MESH_CONF_SEC_AMPE) ||
+            wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa)))
                sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems);
 
        if (!sta) {
index cbecd3714a666ef20e1fd4456462cd81e225b265..5fc1e6184bcb5f6ba69f403655cb2f11c5fa7271 100644 (file)
@@ -19,7 +19,8 @@ void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s,
                              struct sta_info *sta,
                              enum mesh_plink_state state);
 int mesh_mpm_close_peer(struct wpa_supplicant *wpa_s, const u8 *addr);
-int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr);
+int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+                         int duration);
 
 #ifdef CONFIG_MESH
 
index 5d88274b8678ea914e3224f82c7e86f6f2ef223d..7077cc6c4d2c022e1cd300d5d64a0c9e5acafbff 100644 (file)
@@ -291,6 +291,7 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s,
 {
        struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
        struct wpa_ssid *ssid = wpa_s->current_ssid;
+       struct rsn_pmksa_cache_entry *pmksa;
        unsigned int rnd;
        int ret;
 
@@ -306,6 +307,29 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s,
                        return -1;
        }
 
+       pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr);
+       if (pmksa) {
+               if (!sta->wpa_sm)
+                       sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+                                                       sta->addr, NULL);
+               if (!sta->wpa_sm) {
+                       wpa_printf(MSG_ERROR,
+                                  "mesh: Failed to initialize RSN state machine");
+                       return -1;
+               }
+
+               wpa_printf(MSG_DEBUG,
+                          "AUTH: Mesh PMKSA cache entry found for " MACSTR
+                          " - try to use PMKSA caching instead of new SAE authentication",
+                          MAC2STR(sta->addr));
+               wpa_auth_pmksa_set_to_sm(pmksa, sta->wpa_sm, hapd->wpa_auth,
+                                        sta->sae->pmkid, sta->sae->pmk);
+               sae_accept_sta(hapd, sta);
+               sta->mesh_sae_pmksa_caching = 1;
+               return 0;
+       }
+       sta->mesh_sae_pmksa_caching = 0;
+
        if (mesh_rsn_build_sae_commit(wpa_s, ssid, sta))
                return -1;
 
@@ -514,6 +538,17 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
        const size_t aad_len[] = { ETH_ALEN, ETH_ALEN,
                                   (elems->mic - 2) - cat };
 
+       if (!sta->sae) {
+               struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+
+               if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr)) {
+                       wpa_printf(MSG_INFO,
+                                  "Mesh RSN: SAE is not prepared yet");
+                       return -1;
+               }
+               mesh_rsn_auth_sae_sta(wpa_s, sta);
+       }
+
        if (chosen_pmk && os_memcmp(chosen_pmk, sta->sae->pmkid, PMKID_LEN)) {
                wpa_msg(wpa_s, MSG_DEBUG,
                        "Mesh RSN: Invalid PMKID (Chosen PMK did not match calculated PMKID)");
index bd669e339adc13ca75b8c9ff31d40173071b1102..36a7a4ebdcf2afaf23ae9475bcb6efffff01cbc0 100644 (file)
@@ -3229,7 +3229,7 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
          "<addr> = Remove a mesh peer" },
        { "mesh_peer_add", wpa_cli_cmd_mesh_peer_add, NULL,
          cli_cmd_flag_none,
-         "<addr> = Add a mesh peer" },
+         "<addr> [duration=<seconds>] = Add a mesh peer" },
 #endif /* CONFIG_MESH */
 #ifdef CONFIG_P2P
        { "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find,