xref: /freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.c (revision 39beb93c3f8bdbf72a61fda42300b5ebed7390c8)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * WPA Supplicant - RSN PMKSA cache
339beb93cSSam Leffler  * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
539beb93cSSam Leffler  * This program is free software; you can redistribute it and/or modify
639beb93cSSam Leffler  * it under the terms of the GNU General Public License version 2 as
739beb93cSSam Leffler  * published by the Free Software Foundation.
839beb93cSSam Leffler  *
939beb93cSSam Leffler  * Alternatively, this software may be distributed under the terms of BSD
1039beb93cSSam Leffler  * license.
1139beb93cSSam Leffler  *
1239beb93cSSam Leffler  * See README and COPYING for more details.
1339beb93cSSam Leffler  */
1439beb93cSSam Leffler 
1539beb93cSSam Leffler #include "includes.h"
1639beb93cSSam Leffler 
1739beb93cSSam Leffler #include "common.h"
1839beb93cSSam Leffler #include "wpa.h"
1939beb93cSSam Leffler #include "eloop.h"
2039beb93cSSam Leffler #include "sha1.h"
2139beb93cSSam Leffler #include "sha256.h"
2239beb93cSSam Leffler #include "wpa_i.h"
2339beb93cSSam Leffler #include "eapol_supp/eapol_supp_sm.h"
2439beb93cSSam Leffler #include "pmksa_cache.h"
2539beb93cSSam Leffler 
2639beb93cSSam Leffler #if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
2739beb93cSSam Leffler 
2839beb93cSSam Leffler static const int pmksa_cache_max_entries = 32;
2939beb93cSSam Leffler 
3039beb93cSSam Leffler struct rsn_pmksa_cache {
3139beb93cSSam Leffler 	struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */
3239beb93cSSam Leffler 	int pmksa_count; /* number of entries in PMKSA cache */
3339beb93cSSam Leffler 	struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
3439beb93cSSam Leffler 
3539beb93cSSam Leffler 	void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
3639beb93cSSam Leffler 			int replace);
3739beb93cSSam Leffler 	void *ctx;
3839beb93cSSam Leffler };
3939beb93cSSam Leffler 
4039beb93cSSam Leffler 
4139beb93cSSam Leffler /**
4239beb93cSSam Leffler  * rsn_pmkid - Calculate PMK identifier
4339beb93cSSam Leffler  * @pmk: Pairwise master key
4439beb93cSSam Leffler  * @pmk_len: Length of pmk in bytes
4539beb93cSSam Leffler  * @aa: Authenticator address
4639beb93cSSam Leffler  * @spa: Supplicant address
4739beb93cSSam Leffler  * @use_sha256: Whether to use SHA256-based KDF
4839beb93cSSam Leffler  *
4939beb93cSSam Leffler  * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
5039beb93cSSam Leffler  * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA)
5139beb93cSSam Leffler  */
5239beb93cSSam Leffler static void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa,
5339beb93cSSam Leffler 		      const u8 *spa, u8 *pmkid, int use_sha256)
5439beb93cSSam Leffler {
5539beb93cSSam Leffler 	char *title = "PMK Name";
5639beb93cSSam Leffler 	const u8 *addr[3];
5739beb93cSSam Leffler 	const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
5839beb93cSSam Leffler 	unsigned char hash[SHA256_MAC_LEN];
5939beb93cSSam Leffler 
6039beb93cSSam Leffler 	addr[0] = (u8 *) title;
6139beb93cSSam Leffler 	addr[1] = aa;
6239beb93cSSam Leffler 	addr[2] = spa;
6339beb93cSSam Leffler 
6439beb93cSSam Leffler #ifdef CONFIG_IEEE80211W
6539beb93cSSam Leffler 	if (use_sha256)
6639beb93cSSam Leffler 		hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash);
6739beb93cSSam Leffler 	else
6839beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */
6939beb93cSSam Leffler 		hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash);
7039beb93cSSam Leffler 	os_memcpy(pmkid, hash, PMKID_LEN);
7139beb93cSSam Leffler }
7239beb93cSSam Leffler 
7339beb93cSSam Leffler 
7439beb93cSSam Leffler static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
7539beb93cSSam Leffler 
7639beb93cSSam Leffler 
7739beb93cSSam Leffler static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
7839beb93cSSam Leffler {
7939beb93cSSam Leffler 	os_free(entry);
8039beb93cSSam Leffler }
8139beb93cSSam Leffler 
8239beb93cSSam Leffler 
8339beb93cSSam Leffler static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
8439beb93cSSam Leffler 				   struct rsn_pmksa_cache_entry *entry,
8539beb93cSSam Leffler 				   int replace)
8639beb93cSSam Leffler {
8739beb93cSSam Leffler 	pmksa->pmksa_count--;
8839beb93cSSam Leffler 	pmksa->free_cb(entry, pmksa->ctx, replace);
8939beb93cSSam Leffler 	_pmksa_cache_free_entry(entry);
9039beb93cSSam Leffler }
9139beb93cSSam Leffler 
9239beb93cSSam Leffler 
9339beb93cSSam Leffler static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
9439beb93cSSam Leffler {
9539beb93cSSam Leffler 	struct rsn_pmksa_cache *pmksa = eloop_ctx;
9639beb93cSSam Leffler 	struct os_time now;
9739beb93cSSam Leffler 
9839beb93cSSam Leffler 	os_get_time(&now);
9939beb93cSSam Leffler 	while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
10039beb93cSSam Leffler 		struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
10139beb93cSSam Leffler 		pmksa->pmksa = entry->next;
10239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
10339beb93cSSam Leffler 			   MACSTR, MAC2STR(entry->aa));
10439beb93cSSam Leffler 		pmksa_cache_free_entry(pmksa, entry, 0);
10539beb93cSSam Leffler 	}
10639beb93cSSam Leffler 
10739beb93cSSam Leffler 	pmksa_cache_set_expiration(pmksa);
10839beb93cSSam Leffler }
10939beb93cSSam Leffler 
11039beb93cSSam Leffler 
11139beb93cSSam Leffler static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
11239beb93cSSam Leffler {
11339beb93cSSam Leffler 	struct rsn_pmksa_cache *pmksa = eloop_ctx;
11439beb93cSSam Leffler 	pmksa->sm->cur_pmksa = NULL;
11539beb93cSSam Leffler 	eapol_sm_request_reauth(pmksa->sm->eapol);
11639beb93cSSam Leffler }
11739beb93cSSam Leffler 
11839beb93cSSam Leffler 
11939beb93cSSam Leffler static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
12039beb93cSSam Leffler {
12139beb93cSSam Leffler 	int sec;
12239beb93cSSam Leffler 	struct rsn_pmksa_cache_entry *entry;
12339beb93cSSam Leffler 	struct os_time now;
12439beb93cSSam Leffler 
12539beb93cSSam Leffler 	eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
12639beb93cSSam Leffler 	eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL);
12739beb93cSSam Leffler 	if (pmksa->pmksa == NULL)
12839beb93cSSam Leffler 		return;
12939beb93cSSam Leffler 	os_get_time(&now);
13039beb93cSSam Leffler 	sec = pmksa->pmksa->expiration - now.sec;
13139beb93cSSam Leffler 	if (sec < 0)
13239beb93cSSam Leffler 		sec = 0;
13339beb93cSSam Leffler 	eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
13439beb93cSSam Leffler 
13539beb93cSSam Leffler 	entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
13639beb93cSSam Leffler 		pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL);
13739beb93cSSam Leffler 	if (entry) {
13839beb93cSSam Leffler 		sec = pmksa->pmksa->reauth_time - now.sec;
13939beb93cSSam Leffler 		if (sec < 0)
14039beb93cSSam Leffler 			sec = 0;
14139beb93cSSam Leffler 		eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa,
14239beb93cSSam Leffler 				       NULL);
14339beb93cSSam Leffler 	}
14439beb93cSSam Leffler }
14539beb93cSSam Leffler 
14639beb93cSSam Leffler 
14739beb93cSSam Leffler /**
14839beb93cSSam Leffler  * pmksa_cache_add - Add a PMKSA cache entry
14939beb93cSSam Leffler  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
15039beb93cSSam Leffler  * @pmk: The new pairwise master key
15139beb93cSSam Leffler  * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
15239beb93cSSam Leffler  * @aa: Authenticator address
15339beb93cSSam Leffler  * @spa: Supplicant address
15439beb93cSSam Leffler  * @network_ctx: Network configuration context for this PMK
15539beb93cSSam Leffler  * @akmp: WPA_KEY_MGMT_* used in key derivation
15639beb93cSSam Leffler  * Returns: Pointer to the added PMKSA cache entry or %NULL on error
15739beb93cSSam Leffler  *
15839beb93cSSam Leffler  * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
15939beb93cSSam Leffler  * cache. If an old entry is already in the cache for the same Authenticator,
16039beb93cSSam Leffler  * this entry will be replaced with the new entry. PMKID will be calculated
16139beb93cSSam Leffler  * based on the PMK and the driver interface is notified of the new PMKID.
16239beb93cSSam Leffler  */
16339beb93cSSam Leffler struct rsn_pmksa_cache_entry *
16439beb93cSSam Leffler pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
16539beb93cSSam Leffler 		const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
16639beb93cSSam Leffler {
16739beb93cSSam Leffler 	struct rsn_pmksa_cache_entry *entry, *pos, *prev;
16839beb93cSSam Leffler 	struct os_time now;
16939beb93cSSam Leffler 
17039beb93cSSam Leffler 	if (pmksa->sm->proto != WPA_PROTO_RSN || pmk_len > PMK_LEN)
17139beb93cSSam Leffler 		return NULL;
17239beb93cSSam Leffler 
17339beb93cSSam Leffler 	entry = os_zalloc(sizeof(*entry));
17439beb93cSSam Leffler 	if (entry == NULL)
17539beb93cSSam Leffler 		return NULL;
17639beb93cSSam Leffler 	os_memcpy(entry->pmk, pmk, pmk_len);
17739beb93cSSam Leffler 	entry->pmk_len = pmk_len;
17839beb93cSSam Leffler 	rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
17939beb93cSSam Leffler 		  wpa_key_mgmt_sha256(akmp));
18039beb93cSSam Leffler 	os_get_time(&now);
18139beb93cSSam Leffler 	entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
18239beb93cSSam Leffler 	entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
18339beb93cSSam Leffler 		pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
18439beb93cSSam Leffler 	entry->akmp = akmp;
18539beb93cSSam Leffler 	os_memcpy(entry->aa, aa, ETH_ALEN);
18639beb93cSSam Leffler 	entry->network_ctx = network_ctx;
18739beb93cSSam Leffler 
18839beb93cSSam Leffler 	/* Replace an old entry for the same Authenticator (if found) with the
18939beb93cSSam Leffler 	 * new entry */
19039beb93cSSam Leffler 	pos = pmksa->pmksa;
19139beb93cSSam Leffler 	prev = NULL;
19239beb93cSSam Leffler 	while (pos) {
19339beb93cSSam Leffler 		if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
19439beb93cSSam Leffler 			if (pos->pmk_len == pmk_len &&
19539beb93cSSam Leffler 			    os_memcmp(pos->pmk, pmk, pmk_len) == 0 &&
19639beb93cSSam Leffler 			    os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) ==
19739beb93cSSam Leffler 			    0) {
19839beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "WPA: reusing previous "
19939beb93cSSam Leffler 					   "PMKSA entry");
20039beb93cSSam Leffler 				os_free(entry);
20139beb93cSSam Leffler 				return pos;
20239beb93cSSam Leffler 			}
20339beb93cSSam Leffler 			if (prev == NULL)
20439beb93cSSam Leffler 				pmksa->pmksa = pos->next;
20539beb93cSSam Leffler 			else
20639beb93cSSam Leffler 				prev->next = pos->next;
20739beb93cSSam Leffler 			if (pos == pmksa->sm->cur_pmksa) {
20839beb93cSSam Leffler 				/* We are about to replace the current PMKSA
20939beb93cSSam Leffler 				 * cache entry. This happens when the PMKSA
21039beb93cSSam Leffler 				 * caching attempt fails, so we don't want to
21139beb93cSSam Leffler 				 * force pmksa_cache_free_entry() to disconnect
21239beb93cSSam Leffler 				 * at this point. Let's just make sure the old
21339beb93cSSam Leffler 				 * PMKSA cache entry will not be used in the
21439beb93cSSam Leffler 				 * future.
21539beb93cSSam Leffler 				 */
21639beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "RSN: replacing current "
21739beb93cSSam Leffler 					   "PMKSA entry");
21839beb93cSSam Leffler 				pmksa->sm->cur_pmksa = NULL;
21939beb93cSSam Leffler 			}
22039beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
22139beb93cSSam Leffler 				   "the current AP");
22239beb93cSSam Leffler 			pmksa_cache_free_entry(pmksa, pos, 1);
22339beb93cSSam Leffler 			break;
22439beb93cSSam Leffler 		}
22539beb93cSSam Leffler 		prev = pos;
22639beb93cSSam Leffler 		pos = pos->next;
22739beb93cSSam Leffler 	}
22839beb93cSSam Leffler 
22939beb93cSSam Leffler 	if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
23039beb93cSSam Leffler 		/* Remove the oldest entry to make room for the new entry */
23139beb93cSSam Leffler 		pos = pmksa->pmksa;
23239beb93cSSam Leffler 		pmksa->pmksa = pos->next;
23339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
23439beb93cSSam Leffler 			   "entry (for " MACSTR ") to make room for new one",
23539beb93cSSam Leffler 			   MAC2STR(pos->aa));
23639beb93cSSam Leffler 		wpa_sm_remove_pmkid(pmksa->sm, pos->aa, pos->pmkid);
23739beb93cSSam Leffler 		pmksa_cache_free_entry(pmksa, pos, 0);
23839beb93cSSam Leffler 	}
23939beb93cSSam Leffler 
24039beb93cSSam Leffler 	/* Add the new entry; order by expiration time */
24139beb93cSSam Leffler 	pos = pmksa->pmksa;
24239beb93cSSam Leffler 	prev = NULL;
24339beb93cSSam Leffler 	while (pos) {
24439beb93cSSam Leffler 		if (pos->expiration > entry->expiration)
24539beb93cSSam Leffler 			break;
24639beb93cSSam Leffler 		prev = pos;
24739beb93cSSam Leffler 		pos = pos->next;
24839beb93cSSam Leffler 	}
24939beb93cSSam Leffler 	if (prev == NULL) {
25039beb93cSSam Leffler 		entry->next = pmksa->pmksa;
25139beb93cSSam Leffler 		pmksa->pmksa = entry;
25239beb93cSSam Leffler 		pmksa_cache_set_expiration(pmksa);
25339beb93cSSam Leffler 	} else {
25439beb93cSSam Leffler 		entry->next = prev->next;
25539beb93cSSam Leffler 		prev->next = entry;
25639beb93cSSam Leffler 	}
25739beb93cSSam Leffler 	pmksa->pmksa_count++;
25839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
25939beb93cSSam Leffler 		   MAC2STR(entry->aa));
26039beb93cSSam Leffler 	wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid);
26139beb93cSSam Leffler 
26239beb93cSSam Leffler 	return entry;
26339beb93cSSam Leffler }
26439beb93cSSam Leffler 
26539beb93cSSam Leffler 
26639beb93cSSam Leffler /**
26739beb93cSSam Leffler  * pmksa_cache_deinit - Free all entries in PMKSA cache
26839beb93cSSam Leffler  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
26939beb93cSSam Leffler  */
27039beb93cSSam Leffler void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
27139beb93cSSam Leffler {
27239beb93cSSam Leffler 	struct rsn_pmksa_cache_entry *entry, *prev;
27339beb93cSSam Leffler 
27439beb93cSSam Leffler 	if (pmksa == NULL)
27539beb93cSSam Leffler 		return;
27639beb93cSSam Leffler 
27739beb93cSSam Leffler 	entry = pmksa->pmksa;
27839beb93cSSam Leffler 	pmksa->pmksa = NULL;
27939beb93cSSam Leffler 	while (entry) {
28039beb93cSSam Leffler 		prev = entry;
28139beb93cSSam Leffler 		entry = entry->next;
28239beb93cSSam Leffler 		os_free(prev);
28339beb93cSSam Leffler 	}
28439beb93cSSam Leffler 	pmksa_cache_set_expiration(pmksa);
28539beb93cSSam Leffler 	os_free(pmksa);
28639beb93cSSam Leffler }
28739beb93cSSam Leffler 
28839beb93cSSam Leffler 
28939beb93cSSam Leffler /**
29039beb93cSSam Leffler  * pmksa_cache_get - Fetch a PMKSA cache entry
29139beb93cSSam Leffler  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
29239beb93cSSam Leffler  * @aa: Authenticator address or %NULL to match any
29339beb93cSSam Leffler  * @pmkid: PMKID or %NULL to match any
29439beb93cSSam Leffler  * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
29539beb93cSSam Leffler  */
29639beb93cSSam Leffler struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
29739beb93cSSam Leffler 					       const u8 *aa, const u8 *pmkid)
29839beb93cSSam Leffler {
29939beb93cSSam Leffler 	struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
30039beb93cSSam Leffler 	while (entry) {
30139beb93cSSam Leffler 		if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
30239beb93cSSam Leffler 		    (pmkid == NULL ||
30339beb93cSSam Leffler 		     os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
30439beb93cSSam Leffler 			return entry;
30539beb93cSSam Leffler 		entry = entry->next;
30639beb93cSSam Leffler 	}
30739beb93cSSam Leffler 	return NULL;
30839beb93cSSam Leffler }
30939beb93cSSam Leffler 
31039beb93cSSam Leffler 
31139beb93cSSam Leffler /**
31239beb93cSSam Leffler  * pmksa_cache_notify_reconfig - Reconfiguration notification for PMKSA cache
31339beb93cSSam Leffler  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
31439beb93cSSam Leffler  *
31539beb93cSSam Leffler  * Clear references to old data structures when wpa_supplicant is reconfigured.
31639beb93cSSam Leffler  */
31739beb93cSSam Leffler void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
31839beb93cSSam Leffler {
31939beb93cSSam Leffler 	struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
32039beb93cSSam Leffler 	while (entry) {
32139beb93cSSam Leffler 		entry->network_ctx = NULL;
32239beb93cSSam Leffler 		entry = entry->next;
32339beb93cSSam Leffler 	}
32439beb93cSSam Leffler }
32539beb93cSSam Leffler 
32639beb93cSSam Leffler 
32739beb93cSSam Leffler static struct rsn_pmksa_cache_entry *
32839beb93cSSam Leffler pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
32939beb93cSSam Leffler 			const struct rsn_pmksa_cache_entry *old_entry,
33039beb93cSSam Leffler 			const u8 *aa)
33139beb93cSSam Leffler {
33239beb93cSSam Leffler 	struct rsn_pmksa_cache_entry *new_entry;
33339beb93cSSam Leffler 
33439beb93cSSam Leffler 	new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
33539beb93cSSam Leffler 				    aa, pmksa->sm->own_addr,
33639beb93cSSam Leffler 				    old_entry->network_ctx, old_entry->akmp);
33739beb93cSSam Leffler 	if (new_entry == NULL)
33839beb93cSSam Leffler 		return NULL;
33939beb93cSSam Leffler 
34039beb93cSSam Leffler 	/* TODO: reorder entries based on expiration time? */
34139beb93cSSam Leffler 	new_entry->expiration = old_entry->expiration;
34239beb93cSSam Leffler 	new_entry->opportunistic = 1;
34339beb93cSSam Leffler 
34439beb93cSSam Leffler 	return new_entry;
34539beb93cSSam Leffler }
34639beb93cSSam Leffler 
34739beb93cSSam Leffler 
34839beb93cSSam Leffler /**
34939beb93cSSam Leffler  * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry
35039beb93cSSam Leffler  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
35139beb93cSSam Leffler  * @network_ctx: Network configuration context
35239beb93cSSam Leffler  * @aa: Authenticator address for the new AP
35339beb93cSSam Leffler  * Returns: Pointer to a new PMKSA cache entry or %NULL if not available
35439beb93cSSam Leffler  *
35539beb93cSSam Leffler  * Try to create a new PMKSA cache entry opportunistically by guessing that the
35639beb93cSSam Leffler  * new AP is sharing the same PMK as another AP that has the same SSID and has
35739beb93cSSam Leffler  * already an entry in PMKSA cache.
35839beb93cSSam Leffler  */
35939beb93cSSam Leffler struct rsn_pmksa_cache_entry *
36039beb93cSSam Leffler pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
36139beb93cSSam Leffler 			      const u8 *aa)
36239beb93cSSam Leffler {
36339beb93cSSam Leffler 	struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
36439beb93cSSam Leffler 
36539beb93cSSam Leffler 	if (network_ctx == NULL)
36639beb93cSSam Leffler 		return NULL;
36739beb93cSSam Leffler 	while (entry) {
36839beb93cSSam Leffler 		if (entry->network_ctx == network_ctx) {
36939beb93cSSam Leffler 			entry = pmksa_cache_clone_entry(pmksa, entry, aa);
37039beb93cSSam Leffler 			if (entry) {
37139beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "RSN: added "
37239beb93cSSam Leffler 					   "opportunistic PMKSA cache entry "
37339beb93cSSam Leffler 					   "for " MACSTR, MAC2STR(aa));
37439beb93cSSam Leffler 			}
37539beb93cSSam Leffler 			return entry;
37639beb93cSSam Leffler 		}
37739beb93cSSam Leffler 		entry = entry->next;
37839beb93cSSam Leffler 	}
37939beb93cSSam Leffler 	return NULL;
38039beb93cSSam Leffler }
38139beb93cSSam Leffler 
38239beb93cSSam Leffler 
38339beb93cSSam Leffler /**
38439beb93cSSam Leffler  * pmksa_cache_get_current - Get the current used PMKSA entry
38539beb93cSSam Leffler  * @sm: Pointer to WPA state machine data from wpa_sm_init()
38639beb93cSSam Leffler  * Returns: Pointer to the current PMKSA cache entry or %NULL if not available
38739beb93cSSam Leffler  */
38839beb93cSSam Leffler struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm)
38939beb93cSSam Leffler {
39039beb93cSSam Leffler 	if (sm == NULL)
39139beb93cSSam Leffler 		return NULL;
39239beb93cSSam Leffler 	return sm->cur_pmksa;
39339beb93cSSam Leffler }
39439beb93cSSam Leffler 
39539beb93cSSam Leffler 
39639beb93cSSam Leffler /**
39739beb93cSSam Leffler  * pmksa_cache_clear_current - Clear the current PMKSA entry selection
39839beb93cSSam Leffler  * @sm: Pointer to WPA state machine data from wpa_sm_init()
39939beb93cSSam Leffler  */
40039beb93cSSam Leffler void pmksa_cache_clear_current(struct wpa_sm *sm)
40139beb93cSSam Leffler {
40239beb93cSSam Leffler 	if (sm == NULL)
40339beb93cSSam Leffler 		return;
40439beb93cSSam Leffler 	sm->cur_pmksa = NULL;
40539beb93cSSam Leffler }
40639beb93cSSam Leffler 
40739beb93cSSam Leffler 
40839beb93cSSam Leffler /**
40939beb93cSSam Leffler  * pmksa_cache_set_current - Set the current PMKSA entry selection
41039beb93cSSam Leffler  * @sm: Pointer to WPA state machine data from wpa_sm_init()
41139beb93cSSam Leffler  * @pmkid: PMKID for selecting PMKSA or %NULL if not used
41239beb93cSSam Leffler  * @bssid: BSSID for PMKSA or %NULL if not used
41339beb93cSSam Leffler  * @network_ctx: Network configuration context
41439beb93cSSam Leffler  * @try_opportunistic: Whether to allow opportunistic PMKSA caching
41539beb93cSSam Leffler  * Returns: 0 if PMKSA was found or -1 if no matching entry was found
41639beb93cSSam Leffler  */
41739beb93cSSam Leffler int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
41839beb93cSSam Leffler 			    const u8 *bssid, void *network_ctx,
41939beb93cSSam Leffler 			    int try_opportunistic)
42039beb93cSSam Leffler {
42139beb93cSSam Leffler 	struct rsn_pmksa_cache *pmksa = sm->pmksa;
42239beb93cSSam Leffler 	sm->cur_pmksa = NULL;
42339beb93cSSam Leffler 	if (pmkid)
42439beb93cSSam Leffler 		sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid);
42539beb93cSSam Leffler 	if (sm->cur_pmksa == NULL && bssid)
42639beb93cSSam Leffler 		sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL);
42739beb93cSSam Leffler 	if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
42839beb93cSSam Leffler 		sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
42939beb93cSSam Leffler 							      network_ctx,
43039beb93cSSam Leffler 							      bssid);
43139beb93cSSam Leffler 	if (sm->cur_pmksa) {
43239beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "RSN: PMKID",
43339beb93cSSam Leffler 			    sm->cur_pmksa->pmkid, PMKID_LEN);
43439beb93cSSam Leffler 		return 0;
43539beb93cSSam Leffler 	}
43639beb93cSSam Leffler 	return -1;
43739beb93cSSam Leffler }
43839beb93cSSam Leffler 
43939beb93cSSam Leffler 
44039beb93cSSam Leffler /**
44139beb93cSSam Leffler  * pmksa_cache_list - Dump text list of entries in PMKSA cache
44239beb93cSSam Leffler  * @sm: Pointer to WPA state machine data from wpa_sm_init()
44339beb93cSSam Leffler  * @buf: Buffer for the list
44439beb93cSSam Leffler  * @len: Length of the buffer
44539beb93cSSam Leffler  * Returns: number of bytes written to buffer
44639beb93cSSam Leffler  *
44739beb93cSSam Leffler  * This function is used to generate a text format representation of the
44839beb93cSSam Leffler  * current PMKSA cache contents for the ctrl_iface PMKSA command.
44939beb93cSSam Leffler  */
45039beb93cSSam Leffler int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len)
45139beb93cSSam Leffler {
45239beb93cSSam Leffler 	int i, ret;
45339beb93cSSam Leffler 	char *pos = buf;
45439beb93cSSam Leffler 	struct rsn_pmksa_cache_entry *entry;
45539beb93cSSam Leffler 	struct os_time now;
45639beb93cSSam Leffler 
45739beb93cSSam Leffler 	os_get_time(&now);
45839beb93cSSam Leffler 	ret = os_snprintf(pos, buf + len - pos,
45939beb93cSSam Leffler 			  "Index / AA / PMKID / expiration (in seconds) / "
46039beb93cSSam Leffler 			  "opportunistic\n");
46139beb93cSSam Leffler 	if (ret < 0 || ret >= buf + len - pos)
46239beb93cSSam Leffler 		return pos - buf;
46339beb93cSSam Leffler 	pos += ret;
46439beb93cSSam Leffler 	i = 0;
46539beb93cSSam Leffler 	entry = sm->pmksa->pmksa;
46639beb93cSSam Leffler 	while (entry) {
46739beb93cSSam Leffler 		i++;
46839beb93cSSam Leffler 		ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
46939beb93cSSam Leffler 				  i, MAC2STR(entry->aa));
47039beb93cSSam Leffler 		if (ret < 0 || ret >= buf + len - pos)
47139beb93cSSam Leffler 			return pos - buf;
47239beb93cSSam Leffler 		pos += ret;
47339beb93cSSam Leffler 		pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
47439beb93cSSam Leffler 					PMKID_LEN);
47539beb93cSSam Leffler 		ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
47639beb93cSSam Leffler 				  (int) (entry->expiration - now.sec),
47739beb93cSSam Leffler 				  entry->opportunistic);
47839beb93cSSam Leffler 		if (ret < 0 || ret >= buf + len - pos)
47939beb93cSSam Leffler 			return pos - buf;
48039beb93cSSam Leffler 		pos += ret;
48139beb93cSSam Leffler 		entry = entry->next;
48239beb93cSSam Leffler 	}
48339beb93cSSam Leffler 	return pos - buf;
48439beb93cSSam Leffler }
48539beb93cSSam Leffler 
48639beb93cSSam Leffler 
48739beb93cSSam Leffler /**
48839beb93cSSam Leffler  * pmksa_cache_init - Initialize PMKSA cache
48939beb93cSSam Leffler  * @free_cb: Callback function to be called when a PMKSA cache entry is freed
49039beb93cSSam Leffler  * @ctx: Context pointer for free_cb function
49139beb93cSSam Leffler  * @sm: Pointer to WPA state machine data from wpa_sm_init()
49239beb93cSSam Leffler  * Returns: Pointer to PMKSA cache data or %NULL on failure
49339beb93cSSam Leffler  */
49439beb93cSSam Leffler struct rsn_pmksa_cache *
49539beb93cSSam Leffler pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
49639beb93cSSam Leffler 				 void *ctx, int replace),
49739beb93cSSam Leffler 		 void *ctx, struct wpa_sm *sm)
49839beb93cSSam Leffler {
49939beb93cSSam Leffler 	struct rsn_pmksa_cache *pmksa;
50039beb93cSSam Leffler 
50139beb93cSSam Leffler 	pmksa = os_zalloc(sizeof(*pmksa));
50239beb93cSSam Leffler 	if (pmksa) {
50339beb93cSSam Leffler 		pmksa->free_cb = free_cb;
50439beb93cSSam Leffler 		pmksa->ctx = ctx;
50539beb93cSSam Leffler 		pmksa->sm = sm;
50639beb93cSSam Leffler 	}
50739beb93cSSam Leffler 
50839beb93cSSam Leffler 	return pmksa;
50939beb93cSSam Leffler }
51039beb93cSSam Leffler 
51139beb93cSSam Leffler #endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
512