]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
SAE: Do not expire the current PMKSA cache entry
authorJouni Malinen <jouni@codeaurora.org>
Mon, 18 Oct 2021 17:02:35 +0000 (20:02 +0300)
committerJouni Malinen <j@w1.fi>
Mon, 18 Oct 2021 18:20:52 +0000 (21:20 +0300)
There is no convenient mechanism for reauthenticating and generating a
new PMK during an association with SAE. As such, forced PMK update would
mean having to disassociate and reauthenticate which is not really
desired especially when the default PMKLifetime is only 12 hours.

Postpone PMKSA cache entry expiration of the currently used entry with
SAE until the association is lost. In addition, do not try to force the
EAPOL state machine to perform reauthentication for SAE since that won't
work.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
src/rsn_supp/pmksa_cache.c
src/rsn_supp/pmksa_cache.h
src/rsn_supp/wpa.c

index a9952716b3437350eddecd34e36e20cf4868e2c3..0cd5159821bb2e5c5f195d869c0490970d7ade2f 100644 (file)
@@ -26,6 +26,8 @@ struct rsn_pmksa_cache {
 
        void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
                        enum pmksa_free_reason reason);
+       bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
+                             void *ctx);
        void *ctx;
 };
 
@@ -57,14 +59,35 @@ static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
 {
        struct rsn_pmksa_cache *pmksa = eloop_ctx;
        struct os_reltime now;
+       struct rsn_pmksa_cache_entry *prev = NULL, *tmp;
+       struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
 
        os_get_reltime(&now);
-       while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
-               struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
-               pmksa->pmksa = entry->next;
+       while (entry && entry->expiration <= now.sec) {
+               if (wpa_key_mgmt_sae(entry->akmp) &&
+                   pmksa->is_current_cb(entry, pmksa->ctx)) {
+                       /* Do not expire the currently used PMKSA entry for SAE
+                        * since there is no convenient mechanism for
+                        * reauthenticating during an association with SAE. The
+                        * expired entry will be removed after this association
+                        * has been lost. */
+                       wpa_printf(MSG_DEBUG,
+                                  "RSN: postpone PMKSA cache entry expiration for SAE with "
+                                  MACSTR, MAC2STR(entry->aa));
+                       prev = entry;
+                       entry = entry->next;
+                       continue;
+               }
+
                wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
                           MACSTR, MAC2STR(entry->aa));
-               pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE);
+               if (prev)
+                       prev->next = entry->next;
+               else
+                       pmksa->pmksa = entry->next;
+               tmp = entry;
+               entry = entry->next;
+               pmksa_cache_free_entry(pmksa, tmp, PMKSA_EXPIRE);
        }
 
        pmksa_cache_set_expiration(pmksa);
@@ -91,13 +114,32 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
                return;
        os_get_reltime(&now);
        sec = pmksa->pmksa->expiration - now.sec;
-       if (sec < 0)
+       if (sec < 0) {
                sec = 0;
+               if (wpa_key_mgmt_sae(pmksa->pmksa->akmp) &&
+                   pmksa->is_current_cb(pmksa->pmksa, pmksa->ctx)) {
+                       /* Do not continue polling for the current PMKSA entry
+                        * from SAE to expire every second. Use the expiration
+                        * time to the following entry, if any, and wait at
+                        * maximum 10 minutes to check again.
+                        */
+                       entry = pmksa->pmksa->next;
+                       if (entry) {
+                               sec = entry->expiration - now.sec;
+                               if (sec < 0)
+                                       sec = 0;
+                               else if (sec > 600)
+                                       sec = 600;
+                       } else {
+                               sec = 600;
+                       }
+               }
+       }
        eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
 
        entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
                pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, 0);
-       if (entry) {
+       if (entry && !wpa_key_mgmt_sae(entry->akmp)) {
                sec = pmksa->pmksa->reauth_time - now.sec;
                if (sec < 0)
                        sec = 0;
@@ -653,6 +695,8 @@ struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa)
 struct rsn_pmksa_cache *
 pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
                                 void *ctx, enum pmksa_free_reason reason),
+                bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
+                                      void *ctx),
                 void *ctx, struct wpa_sm *sm)
 {
        struct rsn_pmksa_cache *pmksa;
@@ -660,6 +704,7 @@ pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
        pmksa = os_zalloc(sizeof(*pmksa));
        if (pmksa) {
                pmksa->free_cb = free_cb;
+               pmksa->is_current_cb = is_current_cb;
                pmksa->ctx = ctx;
                pmksa->sm = sm;
        }
index 5f460cc062848f471248dc83db6b876f12c6b77e..b801268599a9ba5b8874320500e103f1ecaaa3df 100644 (file)
@@ -59,6 +59,8 @@ enum pmksa_free_reason {
 struct rsn_pmksa_cache *
 pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
                                 void *ctx, enum pmksa_free_reason reason),
+                bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
+                                      void *ctx),
                 void *ctx, struct wpa_sm *sm);
 void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
 struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
@@ -93,6 +95,8 @@ void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa);
 static inline struct rsn_pmksa_cache *
 pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
                                 void *ctx, enum pmksa_free_reason reason),
+                bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
+                                      void *ctx),
                 void *ctx, struct wpa_sm *sm)
 {
        return (void *) -1;
index 1bb9cc6bc44a0ab362452e965efbb18b7fa82f72..8aa649ac45795d17499eb0129d5a81b988db8fc5 100644 (file)
@@ -2903,6 +2903,15 @@ static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
 }
 
 
+static bool wpa_sm_pmksa_is_current_cb(struct rsn_pmksa_cache_entry *entry,
+                                      void *ctx)
+{
+       struct wpa_sm *sm = ctx;
+
+       return sm->cur_pmksa == entry;
+}
+
+
 /**
  * wpa_sm_init - Initialize WPA state machine
  * @ctx: Context pointer for callbacks; this needs to be an allocated buffer
@@ -2926,7 +2935,8 @@ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
        sm->dot11RSNAConfigPMKReauthThreshold = 70;
        sm->dot11RSNAConfigSATimeout = 60;
 
-       sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm);
+       sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb,
+                                    wpa_sm_pmksa_is_current_cb, sm, sm);
        if (sm->pmksa == NULL) {
                wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
                        "RSN: PMKSA cache initialization failed");