139beb93cSSam Leffler /*
239beb93cSSam Leffler * WPA Supplicant - RSN PMKSA cache
35b9c547cSRui Paulo * Copyright (c) 2004-2009, 2011-2015, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler *
5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo * See README for more details.
739beb93cSSam Leffler */
839beb93cSSam Leffler
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler
1139beb93cSSam Leffler #include "common.h"
1239beb93cSSam Leffler #include "eloop.h"
1339beb93cSSam Leffler #include "eapol_supp/eapol_supp_sm.h"
14e28a4053SRui Paulo #include "wpa.h"
15e28a4053SRui Paulo #include "wpa_i.h"
1639beb93cSSam Leffler #include "pmksa_cache.h"
1739beb93cSSam Leffler
18780fb4a2SCy Schubert #if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA)
1939beb93cSSam Leffler
2039beb93cSSam Leffler static const int pmksa_cache_max_entries = 32;
2139beb93cSSam Leffler
2239beb93cSSam Leffler struct rsn_pmksa_cache {
2339beb93cSSam Leffler struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */
2439beb93cSSam Leffler int pmksa_count; /* number of entries in PMKSA cache */
2539beb93cSSam Leffler struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
2639beb93cSSam Leffler
2739beb93cSSam Leffler void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
28f05cddf9SRui Paulo enum pmksa_free_reason reason);
294b72b91aSCy Schubert bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
304b72b91aSCy Schubert void *ctx);
31*a90b9d01SCy Schubert void (*notify_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
3239beb93cSSam Leffler void *ctx;
3339beb93cSSam Leffler };
3439beb93cSSam Leffler
3539beb93cSSam Leffler
3639beb93cSSam Leffler static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
3739beb93cSSam Leffler
3839beb93cSSam Leffler
_pmksa_cache_free_entry(struct rsn_pmksa_cache_entry * entry)3939beb93cSSam Leffler static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
4039beb93cSSam Leffler {
415b9c547cSRui Paulo bin_clear_free(entry, sizeof(*entry));
4239beb93cSSam Leffler }
4339beb93cSSam Leffler
4439beb93cSSam Leffler
pmksa_cache_free_entry(struct rsn_pmksa_cache * pmksa,struct rsn_pmksa_cache_entry * entry,enum pmksa_free_reason reason)4539beb93cSSam Leffler static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
4639beb93cSSam Leffler struct rsn_pmksa_cache_entry *entry,
47f05cddf9SRui Paulo enum pmksa_free_reason reason)
4839beb93cSSam Leffler {
49*a90b9d01SCy Schubert if (pmksa->sm)
5085732ac8SCy Schubert wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
5185732ac8SCy Schubert entry->pmkid,
52*a90b9d01SCy Schubert entry->fils_cache_id_set ?
53*a90b9d01SCy Schubert entry->fils_cache_id : NULL);
5439beb93cSSam Leffler pmksa->pmksa_count--;
55*a90b9d01SCy Schubert if (pmksa->free_cb)
56f05cddf9SRui Paulo pmksa->free_cb(entry, pmksa->ctx, reason);
5739beb93cSSam Leffler _pmksa_cache_free_entry(entry);
5839beb93cSSam Leffler }
5939beb93cSSam Leffler
6039beb93cSSam Leffler
pmksa_cache_remove(struct rsn_pmksa_cache * pmksa,struct rsn_pmksa_cache_entry * entry)61*a90b9d01SCy Schubert void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
62*a90b9d01SCy Schubert struct rsn_pmksa_cache_entry *entry)
63*a90b9d01SCy Schubert {
64*a90b9d01SCy Schubert struct rsn_pmksa_cache_entry *e;
65*a90b9d01SCy Schubert
66*a90b9d01SCy Schubert e = pmksa->pmksa;
67*a90b9d01SCy Schubert while (e) {
68*a90b9d01SCy Schubert if (e == entry) {
69*a90b9d01SCy Schubert pmksa->pmksa = entry->next;
70*a90b9d01SCy Schubert break;
71*a90b9d01SCy Schubert }
72*a90b9d01SCy Schubert if (e->next == entry) {
73*a90b9d01SCy Schubert e->next = entry->next;
74*a90b9d01SCy Schubert break;
75*a90b9d01SCy Schubert }
76*a90b9d01SCy Schubert }
77*a90b9d01SCy Schubert
78*a90b9d01SCy Schubert if (!e) {
79*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
80*a90b9d01SCy Schubert "RSN: Could not remove PMKSA cache entry %p since it is not in the list",
81*a90b9d01SCy Schubert entry);
82*a90b9d01SCy Schubert return;
83*a90b9d01SCy Schubert }
84*a90b9d01SCy Schubert
85*a90b9d01SCy Schubert pmksa_cache_free_entry(pmksa, entry, PMKSA_FREE);
86*a90b9d01SCy Schubert }
87*a90b9d01SCy Schubert
88*a90b9d01SCy Schubert
pmksa_cache_expire(void * eloop_ctx,void * timeout_ctx)8939beb93cSSam Leffler static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
9039beb93cSSam Leffler {
9139beb93cSSam Leffler struct rsn_pmksa_cache *pmksa = eloop_ctx;
925b9c547cSRui Paulo struct os_reltime now;
934b72b91aSCy Schubert struct rsn_pmksa_cache_entry *prev = NULL, *tmp;
944b72b91aSCy Schubert struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
9539beb93cSSam Leffler
965b9c547cSRui Paulo os_get_reltime(&now);
974b72b91aSCy Schubert while (entry && entry->expiration <= now.sec) {
98*a90b9d01SCy Schubert if (wpa_key_mgmt_sae(entry->akmp) && pmksa->is_current_cb &&
994b72b91aSCy Schubert pmksa->is_current_cb(entry, pmksa->ctx)) {
1004b72b91aSCy Schubert /* Do not expire the currently used PMKSA entry for SAE
1014b72b91aSCy Schubert * since there is no convenient mechanism for
1024b72b91aSCy Schubert * reauthenticating during an association with SAE. The
1034b72b91aSCy Schubert * expired entry will be removed after this association
1044b72b91aSCy Schubert * has been lost. */
1054b72b91aSCy Schubert wpa_printf(MSG_DEBUG,
1064b72b91aSCy Schubert "RSN: postpone PMKSA cache entry expiration for SAE with "
1074b72b91aSCy Schubert MACSTR, MAC2STR(entry->aa));
1084b72b91aSCy Schubert prev = entry;
1094b72b91aSCy Schubert entry = entry->next;
1104b72b91aSCy Schubert continue;
1114b72b91aSCy Schubert }
1124b72b91aSCy Schubert
11339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
11439beb93cSSam Leffler MACSTR, MAC2STR(entry->aa));
1154b72b91aSCy Schubert if (prev)
1164b72b91aSCy Schubert prev->next = entry->next;
1174b72b91aSCy Schubert else
1184b72b91aSCy Schubert pmksa->pmksa = entry->next;
1194b72b91aSCy Schubert tmp = entry;
1204b72b91aSCy Schubert entry = entry->next;
1214b72b91aSCy Schubert pmksa_cache_free_entry(pmksa, tmp, PMKSA_EXPIRE);
12239beb93cSSam Leffler }
12339beb93cSSam Leffler
12439beb93cSSam Leffler pmksa_cache_set_expiration(pmksa);
12539beb93cSSam Leffler }
12639beb93cSSam Leffler
12739beb93cSSam Leffler
pmksa_cache_reauth(void * eloop_ctx,void * timeout_ctx)12839beb93cSSam Leffler static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
12939beb93cSSam Leffler {
13039beb93cSSam Leffler struct rsn_pmksa_cache *pmksa = eloop_ctx;
131*a90b9d01SCy Schubert
132*a90b9d01SCy Schubert if (!pmksa->sm)
133*a90b9d01SCy Schubert return;
134*a90b9d01SCy Schubert
135*a90b9d01SCy Schubert if (pmksa->sm->driver_bss_selection) {
136*a90b9d01SCy Schubert struct rsn_pmksa_cache_entry *entry;
137*a90b9d01SCy Schubert
138*a90b9d01SCy Schubert entry = pmksa->sm->cur_pmksa ?
139*a90b9d01SCy Schubert pmksa->sm->cur_pmksa :
140*a90b9d01SCy Schubert pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL,
141*a90b9d01SCy Schubert NULL, 0);
142*a90b9d01SCy Schubert if (entry && wpa_key_mgmt_sae(entry->akmp)) {
143*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
144*a90b9d01SCy Schubert "RSN: remove reauth threshold passed PMKSA from the driver for SAE");
145*a90b9d01SCy Schubert entry->sae_reauth_scheduled = true;
146*a90b9d01SCy Schubert wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx,
147*a90b9d01SCy Schubert entry->aa, entry->pmkid, NULL);
148*a90b9d01SCy Schubert return;
149*a90b9d01SCy Schubert }
150*a90b9d01SCy Schubert }
151*a90b9d01SCy Schubert
15239beb93cSSam Leffler pmksa->sm->cur_pmksa = NULL;
15339beb93cSSam Leffler eapol_sm_request_reauth(pmksa->sm->eapol);
15439beb93cSSam Leffler }
15539beb93cSSam Leffler
15639beb93cSSam Leffler
pmksa_cache_set_expiration(struct rsn_pmksa_cache * pmksa)15739beb93cSSam Leffler static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
15839beb93cSSam Leffler {
15939beb93cSSam Leffler int sec;
16039beb93cSSam Leffler struct rsn_pmksa_cache_entry *entry;
1615b9c547cSRui Paulo struct os_reltime now;
16239beb93cSSam Leffler
16339beb93cSSam Leffler eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
16439beb93cSSam Leffler eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL);
16539beb93cSSam Leffler if (pmksa->pmksa == NULL)
16639beb93cSSam Leffler return;
1675b9c547cSRui Paulo os_get_reltime(&now);
16839beb93cSSam Leffler sec = pmksa->pmksa->expiration - now.sec;
1694b72b91aSCy Schubert if (sec < 0) {
1704b72b91aSCy Schubert sec = 0;
1714b72b91aSCy Schubert if (wpa_key_mgmt_sae(pmksa->pmksa->akmp) &&
172*a90b9d01SCy Schubert pmksa->is_current_cb &&
1734b72b91aSCy Schubert pmksa->is_current_cb(pmksa->pmksa, pmksa->ctx)) {
1744b72b91aSCy Schubert /* Do not continue polling for the current PMKSA entry
1754b72b91aSCy Schubert * from SAE to expire every second. Use the expiration
1764b72b91aSCy Schubert * time to the following entry, if any, and wait at
1774b72b91aSCy Schubert * maximum 10 minutes to check again.
1784b72b91aSCy Schubert */
1794b72b91aSCy Schubert entry = pmksa->pmksa->next;
1804b72b91aSCy Schubert if (entry) {
1814b72b91aSCy Schubert sec = entry->expiration - now.sec;
18239beb93cSSam Leffler if (sec < 0)
18339beb93cSSam Leffler sec = 0;
1844b72b91aSCy Schubert else if (sec > 600)
1854b72b91aSCy Schubert sec = 600;
1864b72b91aSCy Schubert } else {
1874b72b91aSCy Schubert sec = 600;
1884b72b91aSCy Schubert }
1894b72b91aSCy Schubert }
1904b72b91aSCy Schubert }
19139beb93cSSam Leffler eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
19239beb93cSSam Leffler
193*a90b9d01SCy Schubert if (!pmksa->sm)
194*a90b9d01SCy Schubert return;
195*a90b9d01SCy Schubert
19639beb93cSSam Leffler entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
197*a90b9d01SCy Schubert pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, NULL, 0);
198*a90b9d01SCy Schubert if (entry &&
199*a90b9d01SCy Schubert (!wpa_key_mgmt_sae(entry->akmp) ||
200*a90b9d01SCy Schubert (pmksa->sm->driver_bss_selection &&
201*a90b9d01SCy Schubert !entry->sae_reauth_scheduled))) {
20239beb93cSSam Leffler sec = pmksa->pmksa->reauth_time - now.sec;
20339beb93cSSam Leffler if (sec < 0)
20439beb93cSSam Leffler sec = 0;
20539beb93cSSam Leffler eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa,
20639beb93cSSam Leffler NULL);
20739beb93cSSam Leffler }
20839beb93cSSam Leffler }
20939beb93cSSam Leffler
21039beb93cSSam Leffler
21139beb93cSSam Leffler /**
21239beb93cSSam Leffler * pmksa_cache_add - Add a PMKSA cache entry
21339beb93cSSam Leffler * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
21439beb93cSSam Leffler * @pmk: The new pairwise master key
21539beb93cSSam Leffler * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
216780fb4a2SCy Schubert * @pmkid: Calculated PMKID
2175b9c547cSRui Paulo * @kck: Key confirmation key or %NULL if not yet derived
2185b9c547cSRui Paulo * @kck_len: KCK length in bytes
21939beb93cSSam Leffler * @aa: Authenticator address
22039beb93cSSam Leffler * @spa: Supplicant address
22139beb93cSSam Leffler * @network_ctx: Network configuration context for this PMK
22239beb93cSSam Leffler * @akmp: WPA_KEY_MGMT_* used in key derivation
22385732ac8SCy Schubert * @cache_id: Pointer to FILS Cache Identifier or %NULL if not advertised
22439beb93cSSam Leffler * Returns: Pointer to the added PMKSA cache entry or %NULL on error
22539beb93cSSam Leffler *
22639beb93cSSam Leffler * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
22739beb93cSSam Leffler * cache. If an old entry is already in the cache for the same Authenticator,
22839beb93cSSam Leffler * this entry will be replaced with the new entry. PMKID will be calculated
22939beb93cSSam Leffler * based on the PMK and the driver interface is notified of the new PMKID.
23039beb93cSSam Leffler */
23139beb93cSSam Leffler 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,const u8 * cache_id)23239beb93cSSam Leffler pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
233780fb4a2SCy Schubert const u8 *pmkid, const u8 *kck, size_t kck_len,
23485732ac8SCy Schubert const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
23585732ac8SCy Schubert const u8 *cache_id)
23639beb93cSSam Leffler {
23785732ac8SCy Schubert struct rsn_pmksa_cache_entry *entry;
2385b9c547cSRui Paulo struct os_reltime now;
239*a90b9d01SCy Schubert unsigned int pmk_lifetime = 43200;
240*a90b9d01SCy Schubert unsigned int pmk_reauth_threshold = 70;
24139beb93cSSam Leffler
242780fb4a2SCy Schubert if (pmk_len > PMK_LEN_MAX)
24339beb93cSSam Leffler return NULL;
24439beb93cSSam Leffler
245*a90b9d01SCy Schubert if (kck_len > WPA_KCK_MAX_LEN)
246*a90b9d01SCy Schubert return NULL;
247*a90b9d01SCy Schubert
2485b9c547cSRui Paulo if (wpa_key_mgmt_suite_b(akmp) && !kck)
2495b9c547cSRui Paulo return NULL;
2505b9c547cSRui Paulo
25139beb93cSSam Leffler entry = os_zalloc(sizeof(*entry));
25239beb93cSSam Leffler if (entry == NULL)
25339beb93cSSam Leffler return NULL;
25439beb93cSSam Leffler os_memcpy(entry->pmk, pmk, pmk_len);
25539beb93cSSam Leffler entry->pmk_len = pmk_len;
256*a90b9d01SCy Schubert if (kck_len > 0)
257*a90b9d01SCy Schubert os_memcpy(entry->kck, kck, kck_len);
258*a90b9d01SCy Schubert entry->kck_len = kck_len;
259780fb4a2SCy Schubert if (pmkid)
260780fb4a2SCy Schubert os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
261780fb4a2SCy Schubert else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
2625b9c547cSRui Paulo rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
2635b9c547cSRui Paulo else if (wpa_key_mgmt_suite_b(akmp))
2645b9c547cSRui Paulo rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
2655b9c547cSRui Paulo else
26685732ac8SCy Schubert rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
2675b9c547cSRui Paulo os_get_reltime(&now);
268*a90b9d01SCy Schubert if (pmksa->sm) {
269*a90b9d01SCy Schubert pmk_lifetime = pmksa->sm->dot11RSNAConfigPMKLifetime;
270*a90b9d01SCy Schubert pmk_reauth_threshold =
271*a90b9d01SCy Schubert pmksa->sm->dot11RSNAConfigPMKReauthThreshold;
272*a90b9d01SCy Schubert }
273*a90b9d01SCy Schubert entry->expiration = now.sec + pmk_lifetime;
274*a90b9d01SCy Schubert entry->reauth_time = now.sec +
275*a90b9d01SCy Schubert pmk_lifetime * pmk_reauth_threshold / 100;
27639beb93cSSam Leffler entry->akmp = akmp;
27785732ac8SCy Schubert if (cache_id) {
27885732ac8SCy Schubert entry->fils_cache_id_set = 1;
27985732ac8SCy Schubert os_memcpy(entry->fils_cache_id, cache_id, FILS_CACHE_ID_LEN);
28085732ac8SCy Schubert }
28139beb93cSSam Leffler os_memcpy(entry->aa, aa, ETH_ALEN);
282*a90b9d01SCy Schubert os_memcpy(entry->spa, spa, ETH_ALEN);
28339beb93cSSam Leffler entry->network_ctx = network_ctx;
28439beb93cSSam Leffler
28585732ac8SCy Schubert return pmksa_cache_add_entry(pmksa, entry);
28685732ac8SCy Schubert }
28785732ac8SCy Schubert
28885732ac8SCy Schubert
28985732ac8SCy Schubert struct rsn_pmksa_cache_entry *
pmksa_cache_add_entry(struct rsn_pmksa_cache * pmksa,struct rsn_pmksa_cache_entry * entry)29085732ac8SCy Schubert pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
29185732ac8SCy Schubert struct rsn_pmksa_cache_entry *entry)
29285732ac8SCy Schubert {
29385732ac8SCy Schubert struct rsn_pmksa_cache_entry *pos, *prev;
29485732ac8SCy Schubert
29539beb93cSSam Leffler /* Replace an old entry for the same Authenticator (if found) with the
29639beb93cSSam Leffler * new entry */
29739beb93cSSam Leffler pos = pmksa->pmksa;
29839beb93cSSam Leffler prev = NULL;
29939beb93cSSam Leffler while (pos) {
300*a90b9d01SCy Schubert if (ether_addr_equal(entry->aa, pos->aa) &&
301*a90b9d01SCy Schubert ether_addr_equal(entry->spa, pos->spa)) {
30285732ac8SCy Schubert if (pos->pmk_len == entry->pmk_len &&
30385732ac8SCy Schubert os_memcmp_const(pos->pmk, entry->pmk,
30485732ac8SCy Schubert entry->pmk_len) == 0 &&
3055b9c547cSRui Paulo os_memcmp_const(pos->pmkid, entry->pmkid,
3065b9c547cSRui Paulo PMKID_LEN) == 0) {
30739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "WPA: reusing previous "
30839beb93cSSam Leffler "PMKSA entry");
30939beb93cSSam Leffler os_free(entry);
31039beb93cSSam Leffler return pos;
31139beb93cSSam Leffler }
31239beb93cSSam Leffler if (prev == NULL)
31339beb93cSSam Leffler pmksa->pmksa = pos->next;
31439beb93cSSam Leffler else
31539beb93cSSam Leffler prev->next = pos->next;
316f05cddf9SRui Paulo
317f05cddf9SRui Paulo /*
318f05cddf9SRui Paulo * If OKC is used, there may be other PMKSA cache
319f05cddf9SRui Paulo * entries based on the same PMK. These needs to be
320f05cddf9SRui Paulo * flushed so that a new entry can be created based on
3215b9c547cSRui Paulo * the new PMK. Only clear other entries if they have a
3225b9c547cSRui Paulo * matching PMK and this PMK has been used successfully
3235b9c547cSRui Paulo * with the current AP, i.e., if opportunistic flag has
3245b9c547cSRui Paulo * been cleared in wpa_supplicant_key_neg_complete().
325f05cddf9SRui Paulo */
3265b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
3275b9c547cSRui Paulo "the current AP and any PMKSA cache entry "
3285b9c547cSRui Paulo "that was based on the old PMK");
3295b9c547cSRui Paulo if (!pos->opportunistic)
33085732ac8SCy Schubert pmksa_cache_flush(pmksa, entry->network_ctx,
331c1d255d3SCy Schubert pos->pmk, pos->pmk_len,
332c1d255d3SCy Schubert false);
3335b9c547cSRui Paulo pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE);
33439beb93cSSam Leffler break;
33539beb93cSSam Leffler }
33639beb93cSSam Leffler prev = pos;
33739beb93cSSam Leffler pos = pos->next;
33839beb93cSSam Leffler }
33939beb93cSSam Leffler
34039beb93cSSam Leffler if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
34139beb93cSSam Leffler /* Remove the oldest entry to make room for the new entry */
34239beb93cSSam Leffler pos = pmksa->pmksa;
343f05cddf9SRui Paulo
344*a90b9d01SCy Schubert if (pmksa->sm && pos == pmksa->sm->cur_pmksa) {
345f05cddf9SRui Paulo /*
346f05cddf9SRui Paulo * Never remove the current PMKSA cache entry, since
347f05cddf9SRui Paulo * it's in use, and removing it triggers a needless
348f05cddf9SRui Paulo * deauthentication.
349f05cddf9SRui Paulo */
350f05cddf9SRui Paulo pos = pos->next;
351f05cddf9SRui Paulo pmksa->pmksa->next = pos ? pos->next : NULL;
352f05cddf9SRui Paulo } else
35339beb93cSSam Leffler pmksa->pmksa = pos->next;
354f05cddf9SRui Paulo
355f05cddf9SRui Paulo if (pos) {
356f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle "
357f05cddf9SRui Paulo "PMKSA cache entry (for " MACSTR ") to "
358f05cddf9SRui Paulo "make room for new one",
35939beb93cSSam Leffler MAC2STR(pos->aa));
360f05cddf9SRui Paulo pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE);
361f05cddf9SRui Paulo }
36239beb93cSSam Leffler }
36339beb93cSSam Leffler
36439beb93cSSam Leffler /* Add the new entry; order by expiration time */
36539beb93cSSam Leffler pos = pmksa->pmksa;
36639beb93cSSam Leffler prev = NULL;
36739beb93cSSam Leffler while (pos) {
36839beb93cSSam Leffler if (pos->expiration > entry->expiration)
36939beb93cSSam Leffler break;
37039beb93cSSam Leffler prev = pos;
37139beb93cSSam Leffler pos = pos->next;
37239beb93cSSam Leffler }
37339beb93cSSam Leffler if (prev == NULL) {
37439beb93cSSam Leffler entry->next = pmksa->pmksa;
37539beb93cSSam Leffler pmksa->pmksa = entry;
37639beb93cSSam Leffler pmksa_cache_set_expiration(pmksa);
37739beb93cSSam Leffler } else {
37839beb93cSSam Leffler entry->next = prev->next;
37939beb93cSSam Leffler prev->next = entry;
38039beb93cSSam Leffler }
38139beb93cSSam Leffler pmksa->pmksa_count++;
382f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
383*a90b9d01SCy Schubert " spa=" MACSTR " network_ctx=%p akmp=0x%x",
384*a90b9d01SCy Schubert MAC2STR(entry->aa), MAC2STR(entry->spa),
3854bc52338SCy Schubert entry->network_ctx, entry->akmp);
386*a90b9d01SCy Schubert
387*a90b9d01SCy Schubert if (!pmksa->sm)
388*a90b9d01SCy Schubert return entry;
389*a90b9d01SCy Schubert
390*a90b9d01SCy Schubert if (pmksa->notify_cb)
391*a90b9d01SCy Schubert pmksa->notify_cb(entry, pmksa->ctx);
392*a90b9d01SCy Schubert
39385732ac8SCy Schubert wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid,
39485732ac8SCy Schubert entry->fils_cache_id_set ? entry->fils_cache_id : NULL,
395c1d255d3SCy Schubert entry->pmk, entry->pmk_len,
396c1d255d3SCy Schubert pmksa->sm->dot11RSNAConfigPMKLifetime,
397c1d255d3SCy Schubert pmksa->sm->dot11RSNAConfigPMKReauthThreshold,
398c1d255d3SCy Schubert entry->akmp);
39939beb93cSSam Leffler
40039beb93cSSam Leffler return entry;
40139beb93cSSam Leffler }
40239beb93cSSam Leffler
40339beb93cSSam Leffler
40439beb93cSSam Leffler /**
405f05cddf9SRui Paulo * pmksa_cache_flush - Flush PMKSA cache entries for a specific network
406f05cddf9SRui Paulo * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
407f05cddf9SRui Paulo * @network_ctx: Network configuration context or %NULL to flush all entries
408c1d255d3SCy Schubert * @pmk: PMK to match for or %NULL to match all PMKs
4095b9c547cSRui Paulo * @pmk_len: PMK length
410c1d255d3SCy Schubert * @external_only: Flush only PMKSA cache entries configured by external
411c1d255d3SCy Schubert * applications
412f05cddf9SRui Paulo */
pmksa_cache_flush(struct rsn_pmksa_cache * pmksa,void * network_ctx,const u8 * pmk,size_t pmk_len,bool external_only)4135b9c547cSRui Paulo void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
414c1d255d3SCy Schubert const u8 *pmk, size_t pmk_len, bool external_only)
415f05cddf9SRui Paulo {
416f05cddf9SRui Paulo struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp;
417f05cddf9SRui Paulo int removed = 0;
418f05cddf9SRui Paulo
419f05cddf9SRui Paulo entry = pmksa->pmksa;
420f05cddf9SRui Paulo while (entry) {
4215b9c547cSRui Paulo if ((entry->network_ctx == network_ctx ||
4225b9c547cSRui Paulo network_ctx == NULL) &&
4235b9c547cSRui Paulo (pmk == NULL ||
4245b9c547cSRui Paulo (pmk_len == entry->pmk_len &&
425c1d255d3SCy Schubert os_memcmp(pmk, entry->pmk, pmk_len) == 0)) &&
426c1d255d3SCy Schubert (!external_only || entry->external)) {
427f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry "
428f05cddf9SRui Paulo "for " MACSTR, MAC2STR(entry->aa));
429f05cddf9SRui Paulo if (prev)
430f05cddf9SRui Paulo prev->next = entry->next;
431f05cddf9SRui Paulo else
432f05cddf9SRui Paulo pmksa->pmksa = entry->next;
433f05cddf9SRui Paulo tmp = entry;
434f05cddf9SRui Paulo entry = entry->next;
435f05cddf9SRui Paulo pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE);
436f05cddf9SRui Paulo removed++;
437f05cddf9SRui Paulo } else {
438f05cddf9SRui Paulo prev = entry;
439f05cddf9SRui Paulo entry = entry->next;
440f05cddf9SRui Paulo }
441f05cddf9SRui Paulo }
442f05cddf9SRui Paulo if (removed)
443f05cddf9SRui Paulo pmksa_cache_set_expiration(pmksa);
444f05cddf9SRui Paulo }
445f05cddf9SRui Paulo
446f05cddf9SRui Paulo
447f05cddf9SRui Paulo /**
44839beb93cSSam Leffler * pmksa_cache_deinit - Free all entries in PMKSA cache
44939beb93cSSam Leffler * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
45039beb93cSSam Leffler */
pmksa_cache_deinit(struct rsn_pmksa_cache * pmksa)45139beb93cSSam Leffler void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
45239beb93cSSam Leffler {
45339beb93cSSam Leffler struct rsn_pmksa_cache_entry *entry, *prev;
45439beb93cSSam Leffler
45539beb93cSSam Leffler if (pmksa == NULL)
45639beb93cSSam Leffler return;
45739beb93cSSam Leffler
45839beb93cSSam Leffler entry = pmksa->pmksa;
45939beb93cSSam Leffler pmksa->pmksa = NULL;
46039beb93cSSam Leffler while (entry) {
46139beb93cSSam Leffler prev = entry;
46239beb93cSSam Leffler entry = entry->next;
46339beb93cSSam Leffler os_free(prev);
46439beb93cSSam Leffler }
46539beb93cSSam Leffler pmksa_cache_set_expiration(pmksa);
46639beb93cSSam Leffler os_free(pmksa);
46739beb93cSSam Leffler }
46839beb93cSSam Leffler
46939beb93cSSam Leffler
47039beb93cSSam Leffler /**
47139beb93cSSam Leffler * pmksa_cache_get - Fetch a PMKSA cache entry
47239beb93cSSam Leffler * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
47339beb93cSSam Leffler * @aa: Authenticator address or %NULL to match any
47439beb93cSSam Leffler * @pmkid: PMKID or %NULL to match any
475f05cddf9SRui Paulo * @network_ctx: Network context or %NULL to match any
47685732ac8SCy Schubert * @akmp: Specific AKMP to search for or 0 for any
47739beb93cSSam Leffler * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
47839beb93cSSam Leffler */
pmksa_cache_get(struct rsn_pmksa_cache * pmksa,const u8 * aa,const u8 * spa,const u8 * pmkid,const void * network_ctx,int akmp)47939beb93cSSam Leffler struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
480*a90b9d01SCy Schubert const u8 *aa, const u8 *spa,
481*a90b9d01SCy Schubert const u8 *pmkid,
48285732ac8SCy Schubert const void *network_ctx,
48385732ac8SCy Schubert int akmp)
48439beb93cSSam Leffler {
48539beb93cSSam Leffler struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
48639beb93cSSam Leffler while (entry) {
487*a90b9d01SCy Schubert if ((aa == NULL || ether_addr_equal(entry->aa, aa)) &&
488*a90b9d01SCy Schubert (!spa || ether_addr_equal(entry->spa, spa)) &&
48939beb93cSSam Leffler (pmkid == NULL ||
490f05cddf9SRui Paulo os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) &&
49185732ac8SCy Schubert (!akmp || akmp == entry->akmp) &&
492f05cddf9SRui Paulo (network_ctx == NULL || network_ctx == entry->network_ctx))
49339beb93cSSam Leffler return entry;
49439beb93cSSam Leffler entry = entry->next;
49539beb93cSSam Leffler }
49639beb93cSSam Leffler return NULL;
49739beb93cSSam Leffler }
49839beb93cSSam Leffler
49939beb93cSSam Leffler
50039beb93cSSam Leffler static struct rsn_pmksa_cache_entry *
pmksa_cache_clone_entry(struct rsn_pmksa_cache * pmksa,const struct rsn_pmksa_cache_entry * old_entry,const u8 * aa)50139beb93cSSam Leffler pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
50239beb93cSSam Leffler const struct rsn_pmksa_cache_entry *old_entry,
50339beb93cSSam Leffler const u8 *aa)
50439beb93cSSam Leffler {
50539beb93cSSam Leffler struct rsn_pmksa_cache_entry *new_entry;
50685732ac8SCy Schubert os_time_t old_expiration = old_entry->expiration;
5074b72b91aSCy Schubert os_time_t old_reauth_time = old_entry->reauth_time;
508c1d255d3SCy Schubert const u8 *pmkid = NULL;
50939beb93cSSam Leffler
510*a90b9d01SCy Schubert if (!pmksa->sm)
511*a90b9d01SCy Schubert return NULL;
512*a90b9d01SCy Schubert
513c1d255d3SCy Schubert if (wpa_key_mgmt_sae(old_entry->akmp) ||
514c1d255d3SCy Schubert wpa_key_mgmt_fils(old_entry->akmp))
515c1d255d3SCy Schubert pmkid = old_entry->pmkid;
51639beb93cSSam Leffler new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
517*a90b9d01SCy Schubert pmkid, old_entry->kck, old_entry->kck_len,
51839beb93cSSam Leffler aa, pmksa->sm->own_addr,
51985732ac8SCy Schubert old_entry->network_ctx, old_entry->akmp,
52085732ac8SCy Schubert old_entry->fils_cache_id_set ?
52185732ac8SCy Schubert old_entry->fils_cache_id : NULL);
52239beb93cSSam Leffler if (new_entry == NULL)
52339beb93cSSam Leffler return NULL;
52439beb93cSSam Leffler
52539beb93cSSam Leffler /* TODO: reorder entries based on expiration time? */
52685732ac8SCy Schubert new_entry->expiration = old_expiration;
5274b72b91aSCy Schubert new_entry->reauth_time = old_reauth_time;
52839beb93cSSam Leffler new_entry->opportunistic = 1;
52939beb93cSSam Leffler
53039beb93cSSam Leffler return new_entry;
53139beb93cSSam Leffler }
53239beb93cSSam Leffler
53339beb93cSSam Leffler
53439beb93cSSam Leffler /**
53539beb93cSSam Leffler * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry
53639beb93cSSam Leffler * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
53739beb93cSSam Leffler * @network_ctx: Network configuration context
53839beb93cSSam Leffler * @aa: Authenticator address for the new AP
53985732ac8SCy Schubert * @akmp: Specific AKMP to search for or 0 for any
54039beb93cSSam Leffler * Returns: Pointer to a new PMKSA cache entry or %NULL if not available
54139beb93cSSam Leffler *
54239beb93cSSam Leffler * Try to create a new PMKSA cache entry opportunistically by guessing that the
54339beb93cSSam Leffler * new AP is sharing the same PMK as another AP that has the same SSID and has
54439beb93cSSam Leffler * already an entry in PMKSA cache.
54539beb93cSSam Leffler */
54639beb93cSSam Leffler struct rsn_pmksa_cache_entry *
pmksa_cache_get_opportunistic(struct rsn_pmksa_cache * pmksa,void * network_ctx,const u8 * aa,int akmp)54739beb93cSSam Leffler pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
54885732ac8SCy Schubert const u8 *aa, int akmp)
54939beb93cSSam Leffler {
55039beb93cSSam Leffler struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
55139beb93cSSam Leffler
552f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa));
55339beb93cSSam Leffler if (network_ctx == NULL)
55439beb93cSSam Leffler return NULL;
55539beb93cSSam Leffler while (entry) {
55685732ac8SCy Schubert if (entry->network_ctx == network_ctx &&
55785732ac8SCy Schubert (!akmp || entry->akmp == akmp)) {
558c1d255d3SCy Schubert struct os_reltime now;
559c1d255d3SCy Schubert
560c1d255d3SCy Schubert if (wpa_key_mgmt_sae(entry->akmp) &&
561c1d255d3SCy Schubert os_get_reltime(&now) == 0 &&
562c1d255d3SCy Schubert entry->reauth_time < now.sec) {
563c1d255d3SCy Schubert wpa_printf(MSG_DEBUG,
564c1d255d3SCy Schubert "RSN: Do not clone PMKSA cache entry for "
565c1d255d3SCy Schubert MACSTR
566c1d255d3SCy Schubert " since its reauth threshold has passed",
567c1d255d3SCy Schubert MAC2STR(entry->aa));
568c1d255d3SCy Schubert entry = entry->next;
569c1d255d3SCy Schubert continue;
570c1d255d3SCy Schubert }
571c1d255d3SCy Schubert
57239beb93cSSam Leffler entry = pmksa_cache_clone_entry(pmksa, entry, aa);
57339beb93cSSam Leffler if (entry) {
57439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RSN: added "
57539beb93cSSam Leffler "opportunistic PMKSA cache entry "
57639beb93cSSam Leffler "for " MACSTR, MAC2STR(aa));
57739beb93cSSam Leffler }
57839beb93cSSam Leffler return entry;
57939beb93cSSam Leffler }
58039beb93cSSam Leffler entry = entry->next;
58139beb93cSSam Leffler }
58239beb93cSSam Leffler return NULL;
58339beb93cSSam Leffler }
58439beb93cSSam Leffler
58539beb93cSSam Leffler
58685732ac8SCy Schubert static struct rsn_pmksa_cache_entry *
pmksa_cache_get_fils_cache_id(struct rsn_pmksa_cache * pmksa,const void * network_ctx,const u8 * cache_id)58785732ac8SCy Schubert pmksa_cache_get_fils_cache_id(struct rsn_pmksa_cache *pmksa,
58885732ac8SCy Schubert const void *network_ctx, const u8 *cache_id)
58985732ac8SCy Schubert {
59085732ac8SCy Schubert struct rsn_pmksa_cache_entry *entry;
59185732ac8SCy Schubert
59285732ac8SCy Schubert for (entry = pmksa->pmksa; entry; entry = entry->next) {
59385732ac8SCy Schubert if (network_ctx == entry->network_ctx &&
59485732ac8SCy Schubert entry->fils_cache_id_set &&
59585732ac8SCy Schubert os_memcmp(cache_id, entry->fils_cache_id,
59685732ac8SCy Schubert FILS_CACHE_ID_LEN) == 0)
59785732ac8SCy Schubert return entry;
59885732ac8SCy Schubert }
59985732ac8SCy Schubert
60085732ac8SCy Schubert return NULL;
60185732ac8SCy Schubert }
60285732ac8SCy Schubert
60385732ac8SCy Schubert
60439beb93cSSam Leffler /**
60539beb93cSSam Leffler * pmksa_cache_get_current - Get the current used PMKSA entry
60639beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init()
60739beb93cSSam Leffler * Returns: Pointer to the current PMKSA cache entry or %NULL if not available
60839beb93cSSam Leffler */
pmksa_cache_get_current(struct wpa_sm * sm)60939beb93cSSam Leffler struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm)
61039beb93cSSam Leffler {
61139beb93cSSam Leffler if (sm == NULL)
61239beb93cSSam Leffler return NULL;
61339beb93cSSam Leffler return sm->cur_pmksa;
61439beb93cSSam Leffler }
61539beb93cSSam Leffler
61639beb93cSSam Leffler
61739beb93cSSam Leffler /**
61839beb93cSSam Leffler * pmksa_cache_clear_current - Clear the current PMKSA entry selection
61939beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init()
62039beb93cSSam Leffler */
pmksa_cache_clear_current(struct wpa_sm * sm)62139beb93cSSam Leffler void pmksa_cache_clear_current(struct wpa_sm *sm)
62239beb93cSSam Leffler {
62339beb93cSSam Leffler if (sm == NULL)
62439beb93cSSam Leffler return;
625c1d255d3SCy Schubert if (sm->cur_pmksa)
626c1d255d3SCy Schubert wpa_printf(MSG_DEBUG,
627c1d255d3SCy Schubert "RSN: Clear current PMKSA entry selection");
62839beb93cSSam Leffler sm->cur_pmksa = NULL;
62939beb93cSSam Leffler }
63039beb93cSSam Leffler
63139beb93cSSam Leffler
63239beb93cSSam Leffler /**
63339beb93cSSam Leffler * pmksa_cache_set_current - Set the current PMKSA entry selection
63439beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init()
63539beb93cSSam Leffler * @pmkid: PMKID for selecting PMKSA or %NULL if not used
63639beb93cSSam Leffler * @bssid: BSSID for PMKSA or %NULL if not used
63739beb93cSSam Leffler * @network_ctx: Network configuration context
63839beb93cSSam Leffler * @try_opportunistic: Whether to allow opportunistic PMKSA caching
63985732ac8SCy Schubert * @fils_cache_id: Pointer to FILS Cache Identifier or %NULL if not used
640*a90b9d01SCy Schubert * @associated: Whether the device is associated
64139beb93cSSam Leffler * Returns: 0 if PMKSA was found or -1 if no matching entry was found
64239beb93cSSam Leffler */
pmksa_cache_set_current(struct wpa_sm * sm,const u8 * pmkid,const u8 * bssid,void * network_ctx,int try_opportunistic,const u8 * fils_cache_id,int akmp,bool associated)64339beb93cSSam Leffler int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
64439beb93cSSam Leffler const u8 *bssid, void *network_ctx,
64585732ac8SCy Schubert int try_opportunistic, const u8 *fils_cache_id,
646*a90b9d01SCy Schubert int akmp, bool associated)
64739beb93cSSam Leffler {
64839beb93cSSam Leffler struct rsn_pmksa_cache *pmksa = sm->pmksa;
649f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p "
65085732ac8SCy Schubert "try_opportunistic=%d akmp=0x%x",
65185732ac8SCy Schubert network_ctx, try_opportunistic, akmp);
652f05cddf9SRui Paulo if (pmkid)
653f05cddf9SRui Paulo wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID",
654f05cddf9SRui Paulo pmkid, PMKID_LEN);
655f05cddf9SRui Paulo if (bssid)
656f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR,
657f05cddf9SRui Paulo MAC2STR(bssid));
65885732ac8SCy Schubert if (fils_cache_id)
65985732ac8SCy Schubert wpa_printf(MSG_DEBUG,
66085732ac8SCy Schubert "RSN: Search for FILS Cache Identifier %02x%02x",
66185732ac8SCy Schubert fils_cache_id[0], fils_cache_id[1]);
662f05cddf9SRui Paulo
66339beb93cSSam Leffler sm->cur_pmksa = NULL;
66439beb93cSSam Leffler if (pmkid)
665*a90b9d01SCy Schubert sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, sm->own_addr,
666*a90b9d01SCy Schubert pmkid, network_ctx, akmp);
66739beb93cSSam Leffler if (sm->cur_pmksa == NULL && bssid)
668*a90b9d01SCy Schubert sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, sm->own_addr,
669*a90b9d01SCy Schubert NULL, network_ctx, akmp);
67039beb93cSSam Leffler if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
67139beb93cSSam Leffler sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
67239beb93cSSam Leffler network_ctx,
67385732ac8SCy Schubert bssid, akmp);
67485732ac8SCy Schubert if (sm->cur_pmksa == NULL && fils_cache_id)
67585732ac8SCy Schubert sm->cur_pmksa = pmksa_cache_get_fils_cache_id(pmksa,
67685732ac8SCy Schubert network_ctx,
67785732ac8SCy Schubert fils_cache_id);
67839beb93cSSam Leffler if (sm->cur_pmksa) {
679c1d255d3SCy Schubert struct os_reltime now;
680c1d255d3SCy Schubert
681c1d255d3SCy Schubert if (wpa_key_mgmt_sae(sm->cur_pmksa->akmp) &&
682c1d255d3SCy Schubert os_get_reltime(&now) == 0 &&
683c1d255d3SCy Schubert sm->cur_pmksa->reauth_time < now.sec) {
684*a90b9d01SCy Schubert /* Driver-based roaming might have used a PMKSA entry
685*a90b9d01SCy Schubert * that is already past the reauthentication threshold.
686*a90b9d01SCy Schubert * Remove the related PMKID from the driver to avoid
687*a90b9d01SCy Schubert * further uses for this PMKSA, but allow the
688*a90b9d01SCy Schubert * association to continue since the PMKSA has not yet
689*a90b9d01SCy Schubert * expired. */
690*a90b9d01SCy Schubert wpa_sm_remove_pmkid(sm, sm->cur_pmksa->network_ctx,
691*a90b9d01SCy Schubert sm->cur_pmksa->aa,
692*a90b9d01SCy Schubert sm->cur_pmksa->pmkid, NULL);
693*a90b9d01SCy Schubert if (associated) {
694*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
695*a90b9d01SCy Schubert "RSN: Associated with " MACSTR
696*a90b9d01SCy Schubert " using reauth threshold passed PMKSA cache entry",
697*a90b9d01SCy Schubert MAC2STR(sm->cur_pmksa->aa));
698*a90b9d01SCy Schubert } else {
699c1d255d3SCy Schubert wpa_printf(MSG_DEBUG,
700c1d255d3SCy Schubert "RSN: Do not allow PMKSA cache entry for "
701c1d255d3SCy Schubert MACSTR
702c1d255d3SCy Schubert " to be used for SAE since its reauth threshold has passed",
703c1d255d3SCy Schubert MAC2STR(sm->cur_pmksa->aa));
704c1d255d3SCy Schubert sm->cur_pmksa = NULL;
705c1d255d3SCy Schubert return -1;
706c1d255d3SCy Schubert }
707*a90b9d01SCy Schubert }
708c1d255d3SCy Schubert
709f05cddf9SRui Paulo wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID",
71039beb93cSSam Leffler sm->cur_pmksa->pmkid, PMKID_LEN);
71139beb93cSSam Leffler return 0;
71239beb93cSSam Leffler }
713f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found");
71439beb93cSSam Leffler return -1;
71539beb93cSSam Leffler }
71639beb93cSSam Leffler
71739beb93cSSam Leffler
71839beb93cSSam Leffler /**
71939beb93cSSam Leffler * pmksa_cache_list - Dump text list of entries in PMKSA cache
720e28a4053SRui Paulo * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
72139beb93cSSam Leffler * @buf: Buffer for the list
72239beb93cSSam Leffler * @len: Length of the buffer
72339beb93cSSam Leffler * Returns: number of bytes written to buffer
72439beb93cSSam Leffler *
72539beb93cSSam Leffler * This function is used to generate a text format representation of the
72639beb93cSSam Leffler * current PMKSA cache contents for the ctrl_iface PMKSA command.
72739beb93cSSam Leffler */
pmksa_cache_list(struct rsn_pmksa_cache * pmksa,char * buf,size_t len)728e28a4053SRui Paulo int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
72939beb93cSSam Leffler {
73039beb93cSSam Leffler int i, ret;
73139beb93cSSam Leffler char *pos = buf;
73239beb93cSSam Leffler struct rsn_pmksa_cache_entry *entry;
7335b9c547cSRui Paulo struct os_reltime now;
73485732ac8SCy Schubert int cache_id_used = 0;
73585732ac8SCy Schubert
73685732ac8SCy Schubert for (entry = pmksa->pmksa; entry; entry = entry->next) {
73785732ac8SCy Schubert if (entry->fils_cache_id_set) {
73885732ac8SCy Schubert cache_id_used = 1;
73985732ac8SCy Schubert break;
74085732ac8SCy Schubert }
74185732ac8SCy Schubert }
74239beb93cSSam Leffler
7435b9c547cSRui Paulo os_get_reltime(&now);
74439beb93cSSam Leffler ret = os_snprintf(pos, buf + len - pos,
74539beb93cSSam Leffler "Index / AA / PMKID / expiration (in seconds) / "
74685732ac8SCy Schubert "opportunistic%s\n",
74785732ac8SCy Schubert cache_id_used ? " / FILS Cache Identifier" : "");
7485b9c547cSRui Paulo if (os_snprintf_error(buf + len - pos, ret))
74939beb93cSSam Leffler return pos - buf;
75039beb93cSSam Leffler pos += ret;
75139beb93cSSam Leffler i = 0;
752e28a4053SRui Paulo entry = pmksa->pmksa;
75339beb93cSSam Leffler while (entry) {
75439beb93cSSam Leffler i++;
75539beb93cSSam Leffler ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
75639beb93cSSam Leffler i, MAC2STR(entry->aa));
7575b9c547cSRui Paulo if (os_snprintf_error(buf + len - pos, ret))
75839beb93cSSam Leffler return pos - buf;
75939beb93cSSam Leffler pos += ret;
76039beb93cSSam Leffler pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
76139beb93cSSam Leffler PMKID_LEN);
76285732ac8SCy Schubert ret = os_snprintf(pos, buf + len - pos, " %d %d",
76339beb93cSSam Leffler (int) (entry->expiration - now.sec),
76439beb93cSSam Leffler entry->opportunistic);
7655b9c547cSRui Paulo if (os_snprintf_error(buf + len - pos, ret))
76639beb93cSSam Leffler return pos - buf;
76739beb93cSSam Leffler pos += ret;
76885732ac8SCy Schubert if (entry->fils_cache_id_set) {
76985732ac8SCy Schubert ret = os_snprintf(pos, buf + len - pos, " %02x%02x",
77085732ac8SCy Schubert entry->fils_cache_id[0],
77185732ac8SCy Schubert entry->fils_cache_id[1]);
77285732ac8SCy Schubert if (os_snprintf_error(buf + len - pos, ret))
77385732ac8SCy Schubert return pos - buf;
77485732ac8SCy Schubert pos += ret;
77585732ac8SCy Schubert }
77685732ac8SCy Schubert ret = os_snprintf(pos, buf + len - pos, "\n");
77785732ac8SCy Schubert if (os_snprintf_error(buf + len - pos, ret))
77885732ac8SCy Schubert return pos - buf;
77985732ac8SCy Schubert pos += ret;
78039beb93cSSam Leffler entry = entry->next;
78139beb93cSSam Leffler }
78239beb93cSSam Leffler return pos - buf;
78339beb93cSSam Leffler }
78439beb93cSSam Leffler
78539beb93cSSam Leffler
pmksa_cache_head(struct rsn_pmksa_cache * pmksa)78685732ac8SCy Schubert struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa)
78785732ac8SCy Schubert {
78885732ac8SCy Schubert return pmksa->pmksa;
78985732ac8SCy Schubert }
79085732ac8SCy Schubert
79185732ac8SCy Schubert
79239beb93cSSam Leffler /**
79339beb93cSSam Leffler * pmksa_cache_init - Initialize PMKSA cache
79439beb93cSSam Leffler * @free_cb: Callback function to be called when a PMKSA cache entry is freed
79539beb93cSSam Leffler * @ctx: Context pointer for free_cb function
79639beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init()
79739beb93cSSam Leffler * Returns: Pointer to PMKSA cache data or %NULL on failure
79839beb93cSSam Leffler */
79939beb93cSSam Leffler 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 (* notify_cb)(struct rsn_pmksa_cache_entry * entry,void * ctx),void * ctx,struct wpa_sm * sm)80039beb93cSSam Leffler pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
801f05cddf9SRui Paulo void *ctx, enum pmksa_free_reason reason),
8024b72b91aSCy Schubert bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
8034b72b91aSCy Schubert void *ctx),
804*a90b9d01SCy Schubert void (*notify_cb)(struct rsn_pmksa_cache_entry *entry,
805*a90b9d01SCy Schubert void *ctx),
80639beb93cSSam Leffler void *ctx, struct wpa_sm *sm)
80739beb93cSSam Leffler {
80839beb93cSSam Leffler struct rsn_pmksa_cache *pmksa;
80939beb93cSSam Leffler
81039beb93cSSam Leffler pmksa = os_zalloc(sizeof(*pmksa));
81139beb93cSSam Leffler if (pmksa) {
81239beb93cSSam Leffler pmksa->free_cb = free_cb;
8134b72b91aSCy Schubert pmksa->is_current_cb = is_current_cb;
814*a90b9d01SCy Schubert pmksa->notify_cb = notify_cb;
81539beb93cSSam Leffler pmksa->ctx = ctx;
81639beb93cSSam Leffler pmksa->sm = sm;
81739beb93cSSam Leffler }
81839beb93cSSam Leffler
81939beb93cSSam Leffler return pmksa;
82039beb93cSSam Leffler }
82139beb93cSSam Leffler
8224b72b91aSCy Schubert
pmksa_cache_reconfig(struct rsn_pmksa_cache * pmksa)8234b72b91aSCy Schubert void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa)
8244b72b91aSCy Schubert {
8254b72b91aSCy Schubert struct rsn_pmksa_cache_entry *entry;
8264b72b91aSCy Schubert struct os_reltime now;
8274b72b91aSCy Schubert
8284b72b91aSCy Schubert if (!pmksa || !pmksa->pmksa)
8294b72b91aSCy Schubert return;
8304b72b91aSCy Schubert
8314b72b91aSCy Schubert os_get_reltime(&now);
8324b72b91aSCy Schubert for (entry = pmksa->pmksa; entry; entry = entry->next) {
8334b72b91aSCy Schubert u32 life_time;
8344b72b91aSCy Schubert u8 reauth_threshold;
8354b72b91aSCy Schubert
8364b72b91aSCy Schubert if (entry->expiration - now.sec < 1 ||
8374b72b91aSCy Schubert entry->reauth_time - now.sec < 1)
8384b72b91aSCy Schubert continue;
8394b72b91aSCy Schubert
8404b72b91aSCy Schubert life_time = entry->expiration - now.sec;
8414b72b91aSCy Schubert reauth_threshold = (entry->reauth_time - now.sec) * 100 /
8424b72b91aSCy Schubert life_time;
8434b72b91aSCy Schubert if (!reauth_threshold)
8444b72b91aSCy Schubert continue;
8454b72b91aSCy Schubert
8464b72b91aSCy Schubert wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
8474b72b91aSCy Schubert entry->pmkid,
8484b72b91aSCy Schubert entry->fils_cache_id_set ?
8494b72b91aSCy Schubert entry->fils_cache_id : NULL,
8504b72b91aSCy Schubert entry->pmk, entry->pmk_len, life_time,
8514b72b91aSCy Schubert reauth_threshold, entry->akmp);
8524b72b91aSCy Schubert }
8534b72b91aSCy Schubert }
8544b72b91aSCy Schubert
855*a90b9d01SCy Schubert #else /* IEEE8021X_EAPOL */
856*a90b9d01SCy Schubert
857*a90b9d01SCy Schubert 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 (* notify_cb)(struct rsn_pmksa_cache_entry * entry,void * ctx),void * ctx,struct wpa_sm * sm)858*a90b9d01SCy Schubert pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
859*a90b9d01SCy Schubert void *ctx, enum pmksa_free_reason reason),
860*a90b9d01SCy Schubert bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
861*a90b9d01SCy Schubert void *ctx),
862*a90b9d01SCy Schubert void (*notify_cb)(struct rsn_pmksa_cache_entry *entry,
863*a90b9d01SCy Schubert void *ctx),
864*a90b9d01SCy Schubert void *ctx, struct wpa_sm *sm)
865*a90b9d01SCy Schubert {
866*a90b9d01SCy Schubert return (void *) -1;
867*a90b9d01SCy Schubert }
868*a90b9d01SCy Schubert
869*a90b9d01SCy Schubert
pmksa_cache_deinit(struct rsn_pmksa_cache * pmksa)870*a90b9d01SCy Schubert void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
871*a90b9d01SCy Schubert {
872*a90b9d01SCy Schubert }
873*a90b9d01SCy Schubert
874*a90b9d01SCy Schubert
875*a90b9d01SCy Schubert struct rsn_pmksa_cache_entry *
pmksa_cache_get(struct rsn_pmksa_cache * pmksa,const u8 * aa,const u8 * spa,const u8 * pmkid,const void * network_ctx,int akmp)876*a90b9d01SCy Schubert pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
877*a90b9d01SCy Schubert const u8 *pmkid, const void *network_ctx, int akmp)
878*a90b9d01SCy Schubert {
879*a90b9d01SCy Schubert return NULL;
880*a90b9d01SCy Schubert }
881*a90b9d01SCy Schubert
882*a90b9d01SCy Schubert
883*a90b9d01SCy Schubert struct rsn_pmksa_cache_entry *
pmksa_cache_get_current(struct wpa_sm * sm)884*a90b9d01SCy Schubert pmksa_cache_get_current(struct wpa_sm *sm)
885*a90b9d01SCy Schubert {
886*a90b9d01SCy Schubert return NULL;
887*a90b9d01SCy Schubert }
888*a90b9d01SCy Schubert
889*a90b9d01SCy Schubert
pmksa_cache_list(struct rsn_pmksa_cache * pmksa,char * buf,size_t len)890*a90b9d01SCy Schubert int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
891*a90b9d01SCy Schubert {
892*a90b9d01SCy Schubert return -1;
893*a90b9d01SCy Schubert }
894*a90b9d01SCy Schubert
895*a90b9d01SCy Schubert
896*a90b9d01SCy Schubert struct rsn_pmksa_cache_entry *
pmksa_cache_head(struct rsn_pmksa_cache * pmksa)897*a90b9d01SCy Schubert pmksa_cache_head(struct rsn_pmksa_cache *pmksa)
898*a90b9d01SCy Schubert {
899*a90b9d01SCy Schubert return NULL;
900*a90b9d01SCy Schubert }
901*a90b9d01SCy Schubert
902*a90b9d01SCy Schubert
903*a90b9d01SCy Schubert struct rsn_pmksa_cache_entry *
pmksa_cache_add_entry(struct rsn_pmksa_cache * pmksa,struct rsn_pmksa_cache_entry * entry)904*a90b9d01SCy Schubert pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
905*a90b9d01SCy Schubert struct rsn_pmksa_cache_entry *entry)
906*a90b9d01SCy Schubert {
907*a90b9d01SCy Schubert return NULL;
908*a90b9d01SCy Schubert }
909*a90b9d01SCy Schubert
910*a90b9d01SCy Schubert
911*a90b9d01SCy Schubert 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,const u8 * cache_id)912*a90b9d01SCy Schubert pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
913*a90b9d01SCy Schubert const u8 *pmkid, const u8 *kck, size_t kck_len,
914*a90b9d01SCy Schubert const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
915*a90b9d01SCy Schubert const u8 *cache_id)
916*a90b9d01SCy Schubert {
917*a90b9d01SCy Schubert return NULL;
918*a90b9d01SCy Schubert }
919*a90b9d01SCy Schubert
920*a90b9d01SCy Schubert
pmksa_cache_clear_current(struct wpa_sm * sm)921*a90b9d01SCy Schubert void pmksa_cache_clear_current(struct wpa_sm *sm)
922*a90b9d01SCy Schubert {
923*a90b9d01SCy Schubert }
924*a90b9d01SCy Schubert
925*a90b9d01SCy Schubert
pmksa_cache_set_current(struct wpa_sm * sm,const u8 * pmkid,const u8 * bssid,void * network_ctx,int try_opportunistic,const u8 * fils_cache_id,int akmp,bool associated)926*a90b9d01SCy Schubert int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, const u8 *bssid,
927*a90b9d01SCy Schubert void *network_ctx, int try_opportunistic,
928*a90b9d01SCy Schubert const u8 *fils_cache_id, int akmp, bool associated)
929*a90b9d01SCy Schubert {
930*a90b9d01SCy Schubert return -1;
931*a90b9d01SCy Schubert }
932*a90b9d01SCy Schubert
933*a90b9d01SCy Schubert
pmksa_cache_flush(struct rsn_pmksa_cache * pmksa,void * network_ctx,const u8 * pmk,size_t pmk_len,bool external_only)934*a90b9d01SCy Schubert void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
935*a90b9d01SCy Schubert const u8 *pmk, size_t pmk_len, bool external_only)
936*a90b9d01SCy Schubert {
937*a90b9d01SCy Schubert }
938*a90b9d01SCy Schubert
939*a90b9d01SCy Schubert
pmksa_cache_remove(struct rsn_pmksa_cache * pmksa,struct rsn_pmksa_cache_entry * entry)940*a90b9d01SCy Schubert void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
941*a90b9d01SCy Schubert struct rsn_pmksa_cache_entry *entry)
942*a90b9d01SCy Schubert {
943*a90b9d01SCy Schubert }
944*a90b9d01SCy Schubert
945*a90b9d01SCy Schubert
pmksa_cache_reconfig(struct rsn_pmksa_cache * pmksa)946*a90b9d01SCy Schubert void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa)
947*a90b9d01SCy Schubert {
948*a90b9d01SCy Schubert }
949*a90b9d01SCy Schubert
9505b9c547cSRui Paulo #endif /* IEEE8021X_EAPOL */
951