xref: /freebsd/contrib/wpa/src/ap/pmksa_cache_auth.c (revision f05cddf940dbfc5b657f5e9beb9de2c31e509e5b)
1e28a4053SRui Paulo /*
2e28a4053SRui Paulo  * hostapd - PMKSA cache for IEEE 802.11i RSN
3*f05cddf9SRui Paulo  * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
4e28a4053SRui Paulo  *
5*f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6*f05cddf9SRui Paulo  * See README for more details.
7e28a4053SRui Paulo  */
8e28a4053SRui Paulo 
9e28a4053SRui Paulo #include "utils/includes.h"
10e28a4053SRui Paulo 
11e28a4053SRui Paulo #include "utils/common.h"
12e28a4053SRui Paulo #include "utils/eloop.h"
13e28a4053SRui Paulo #include "eapol_auth/eapol_auth_sm.h"
14e28a4053SRui Paulo #include "eapol_auth/eapol_auth_sm_i.h"
15e28a4053SRui Paulo #include "sta_info.h"
16e28a4053SRui Paulo #include "ap_config.h"
17e28a4053SRui Paulo #include "pmksa_cache_auth.h"
18e28a4053SRui Paulo 
19e28a4053SRui Paulo 
20e28a4053SRui Paulo static const int pmksa_cache_max_entries = 1024;
21e28a4053SRui Paulo static const int dot11RSNAConfigPMKLifetime = 43200;
22e28a4053SRui Paulo 
23e28a4053SRui Paulo struct rsn_pmksa_cache {
24e28a4053SRui Paulo #define PMKID_HASH_SIZE 128
25e28a4053SRui Paulo #define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f)
26e28a4053SRui Paulo 	struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE];
27e28a4053SRui Paulo 	struct rsn_pmksa_cache_entry *pmksa;
28e28a4053SRui Paulo 	int pmksa_count;
29e28a4053SRui Paulo 
30e28a4053SRui Paulo 	void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
31e28a4053SRui Paulo 	void *ctx;
32e28a4053SRui Paulo };
33e28a4053SRui Paulo 
34e28a4053SRui Paulo 
35e28a4053SRui Paulo static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
36e28a4053SRui Paulo 
37e28a4053SRui Paulo 
38e28a4053SRui Paulo static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
39e28a4053SRui Paulo {
40e28a4053SRui Paulo 	if (entry == NULL)
41e28a4053SRui Paulo 		return;
42e28a4053SRui Paulo 	os_free(entry->identity);
43*f05cddf9SRui Paulo 	wpabuf_free(entry->cui);
44e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
45e28a4053SRui Paulo 	radius_free_class(&entry->radius_class);
46e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
47e28a4053SRui Paulo 	os_free(entry);
48e28a4053SRui Paulo }
49e28a4053SRui Paulo 
50e28a4053SRui Paulo 
51e28a4053SRui Paulo static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
52e28a4053SRui Paulo 				   struct rsn_pmksa_cache_entry *entry)
53e28a4053SRui Paulo {
54e28a4053SRui Paulo 	struct rsn_pmksa_cache_entry *pos, *prev;
55e28a4053SRui Paulo 
56e28a4053SRui Paulo 	pmksa->pmksa_count--;
57e28a4053SRui Paulo 	pmksa->free_cb(entry, pmksa->ctx);
58e28a4053SRui Paulo 	pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
59e28a4053SRui Paulo 	prev = NULL;
60e28a4053SRui Paulo 	while (pos) {
61e28a4053SRui Paulo 		if (pos == entry) {
62e28a4053SRui Paulo 			if (prev != NULL) {
63e28a4053SRui Paulo 				prev->hnext = pos->hnext;
64e28a4053SRui Paulo 			} else {
65e28a4053SRui Paulo 				pmksa->pmkid[PMKID_HASH(entry->pmkid)] =
66e28a4053SRui Paulo 					pos->hnext;
67e28a4053SRui Paulo 			}
68e28a4053SRui Paulo 			break;
69e28a4053SRui Paulo 		}
70e28a4053SRui Paulo 		prev = pos;
71e28a4053SRui Paulo 		pos = pos->hnext;
72e28a4053SRui Paulo 	}
73e28a4053SRui Paulo 
74e28a4053SRui Paulo 	pos = pmksa->pmksa;
75e28a4053SRui Paulo 	prev = NULL;
76e28a4053SRui Paulo 	while (pos) {
77e28a4053SRui Paulo 		if (pos == entry) {
78e28a4053SRui Paulo 			if (prev != NULL)
79e28a4053SRui Paulo 				prev->next = pos->next;
80e28a4053SRui Paulo 			else
81e28a4053SRui Paulo 				pmksa->pmksa = pos->next;
82e28a4053SRui Paulo 			break;
83e28a4053SRui Paulo 		}
84e28a4053SRui Paulo 		prev = pos;
85e28a4053SRui Paulo 		pos = pos->next;
86e28a4053SRui Paulo 	}
87e28a4053SRui Paulo 	_pmksa_cache_free_entry(entry);
88e28a4053SRui Paulo }
89e28a4053SRui Paulo 
90e28a4053SRui Paulo 
91e28a4053SRui Paulo static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
92e28a4053SRui Paulo {
93e28a4053SRui Paulo 	struct rsn_pmksa_cache *pmksa = eloop_ctx;
94e28a4053SRui Paulo 	struct os_time now;
95e28a4053SRui Paulo 
96e28a4053SRui Paulo 	os_get_time(&now);
97e28a4053SRui Paulo 	while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
98e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
99*f05cddf9SRui Paulo 			   MACSTR, MAC2STR(pmksa->pmksa->spa));
100*f05cddf9SRui Paulo 		pmksa_cache_free_entry(pmksa, pmksa->pmksa);
101e28a4053SRui Paulo 	}
102e28a4053SRui Paulo 
103e28a4053SRui Paulo 	pmksa_cache_set_expiration(pmksa);
104e28a4053SRui Paulo }
105e28a4053SRui Paulo 
106e28a4053SRui Paulo 
107e28a4053SRui Paulo static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
108e28a4053SRui Paulo {
109e28a4053SRui Paulo 	int sec;
110e28a4053SRui Paulo 	struct os_time now;
111e28a4053SRui Paulo 
112e28a4053SRui Paulo 	eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
113e28a4053SRui Paulo 	if (pmksa->pmksa == NULL)
114e28a4053SRui Paulo 		return;
115e28a4053SRui Paulo 	os_get_time(&now);
116e28a4053SRui Paulo 	sec = pmksa->pmksa->expiration - now.sec;
117e28a4053SRui Paulo 	if (sec < 0)
118e28a4053SRui Paulo 		sec = 0;
119e28a4053SRui Paulo 	eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
120e28a4053SRui Paulo }
121e28a4053SRui Paulo 
122e28a4053SRui Paulo 
123e28a4053SRui Paulo static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
124e28a4053SRui Paulo 					struct eapol_state_machine *eapol)
125e28a4053SRui Paulo {
126e28a4053SRui Paulo 	if (eapol == NULL)
127e28a4053SRui Paulo 		return;
128e28a4053SRui Paulo 
129e28a4053SRui Paulo 	if (eapol->identity) {
130e28a4053SRui Paulo 		entry->identity = os_malloc(eapol->identity_len);
131e28a4053SRui Paulo 		if (entry->identity) {
132e28a4053SRui Paulo 			entry->identity_len = eapol->identity_len;
133e28a4053SRui Paulo 			os_memcpy(entry->identity, eapol->identity,
134e28a4053SRui Paulo 				  eapol->identity_len);
135e28a4053SRui Paulo 		}
136e28a4053SRui Paulo 	}
137e28a4053SRui Paulo 
138*f05cddf9SRui Paulo 	if (eapol->radius_cui)
139*f05cddf9SRui Paulo 		entry->cui = wpabuf_dup(eapol->radius_cui);
140*f05cddf9SRui Paulo 
141e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
142e28a4053SRui Paulo 	radius_copy_class(&entry->radius_class, &eapol->radius_class);
143e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
144e28a4053SRui Paulo 
145e28a4053SRui Paulo 	entry->eap_type_authsrv = eapol->eap_type_authsrv;
146e28a4053SRui Paulo 	entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id;
147e28a4053SRui Paulo }
148e28a4053SRui Paulo 
149e28a4053SRui Paulo 
150e28a4053SRui Paulo void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
151e28a4053SRui Paulo 			       struct eapol_state_machine *eapol)
152e28a4053SRui Paulo {
153e28a4053SRui Paulo 	if (entry == NULL || eapol == NULL)
154e28a4053SRui Paulo 		return;
155e28a4053SRui Paulo 
156e28a4053SRui Paulo 	if (entry->identity) {
157e28a4053SRui Paulo 		os_free(eapol->identity);
158e28a4053SRui Paulo 		eapol->identity = os_malloc(entry->identity_len);
159e28a4053SRui Paulo 		if (eapol->identity) {
160e28a4053SRui Paulo 			eapol->identity_len = entry->identity_len;
161e28a4053SRui Paulo 			os_memcpy(eapol->identity, entry->identity,
162e28a4053SRui Paulo 				  entry->identity_len);
163e28a4053SRui Paulo 		}
164e28a4053SRui Paulo 		wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA",
165e28a4053SRui Paulo 				  eapol->identity, eapol->identity_len);
166e28a4053SRui Paulo 	}
167e28a4053SRui Paulo 
168*f05cddf9SRui Paulo 	if (entry->cui) {
169*f05cddf9SRui Paulo 		wpabuf_free(eapol->radius_cui);
170*f05cddf9SRui Paulo 		eapol->radius_cui = wpabuf_dup(entry->cui);
171*f05cddf9SRui Paulo 	}
172*f05cddf9SRui Paulo 
173e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
174e28a4053SRui Paulo 	radius_free_class(&eapol->radius_class);
175e28a4053SRui Paulo 	radius_copy_class(&eapol->radius_class, &entry->radius_class);
176e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
177e28a4053SRui Paulo 	if (eapol->radius_class.attr) {
178e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from "
179e28a4053SRui Paulo 			   "PMKSA", (unsigned long) eapol->radius_class.count);
180e28a4053SRui Paulo 	}
181e28a4053SRui Paulo 
182e28a4053SRui Paulo 	eapol->eap_type_authsrv = entry->eap_type_authsrv;
183e28a4053SRui Paulo 	((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id;
184e28a4053SRui Paulo }
185e28a4053SRui Paulo 
186e28a4053SRui Paulo 
187e28a4053SRui Paulo static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
188e28a4053SRui Paulo 				   struct rsn_pmksa_cache_entry *entry)
189e28a4053SRui Paulo {
190e28a4053SRui Paulo 	struct rsn_pmksa_cache_entry *pos, *prev;
191e28a4053SRui Paulo 
192e28a4053SRui Paulo 	/* Add the new entry; order by expiration time */
193e28a4053SRui Paulo 	pos = pmksa->pmksa;
194e28a4053SRui Paulo 	prev = NULL;
195e28a4053SRui Paulo 	while (pos) {
196e28a4053SRui Paulo 		if (pos->expiration > entry->expiration)
197e28a4053SRui Paulo 			break;
198e28a4053SRui Paulo 		prev = pos;
199e28a4053SRui Paulo 		pos = pos->next;
200e28a4053SRui Paulo 	}
201e28a4053SRui Paulo 	if (prev == NULL) {
202e28a4053SRui Paulo 		entry->next = pmksa->pmksa;
203e28a4053SRui Paulo 		pmksa->pmksa = entry;
204e28a4053SRui Paulo 	} else {
205e28a4053SRui Paulo 		entry->next = prev->next;
206e28a4053SRui Paulo 		prev->next = entry;
207e28a4053SRui Paulo 	}
208e28a4053SRui Paulo 	entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
209e28a4053SRui Paulo 	pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry;
210e28a4053SRui Paulo 
211e28a4053SRui Paulo 	pmksa->pmksa_count++;
212*f05cddf9SRui Paulo 	if (prev == NULL)
213*f05cddf9SRui Paulo 		pmksa_cache_set_expiration(pmksa);
214e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
215e28a4053SRui Paulo 		   MAC2STR(entry->spa));
216e28a4053SRui Paulo 	wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
217e28a4053SRui Paulo }
218e28a4053SRui Paulo 
219e28a4053SRui Paulo 
220e28a4053SRui Paulo /**
221e28a4053SRui Paulo  * pmksa_cache_auth_add - Add a PMKSA cache entry
222e28a4053SRui Paulo  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
223e28a4053SRui Paulo  * @pmk: The new pairwise master key
224e28a4053SRui Paulo  * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
225e28a4053SRui Paulo  * @aa: Authenticator address
226e28a4053SRui Paulo  * @spa: Supplicant address
227e28a4053SRui Paulo  * @session_timeout: Session timeout
228e28a4053SRui Paulo  * @eapol: Pointer to EAPOL state machine data
229e28a4053SRui Paulo  * @akmp: WPA_KEY_MGMT_* used in key derivation
230e28a4053SRui Paulo  * Returns: Pointer to the added PMKSA cache entry or %NULL on error
231e28a4053SRui Paulo  *
232e28a4053SRui Paulo  * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
233e28a4053SRui Paulo  * cache. If an old entry is already in the cache for the same Supplicant,
234e28a4053SRui Paulo  * this entry will be replaced with the new entry. PMKID will be calculated
235e28a4053SRui Paulo  * based on the PMK.
236e28a4053SRui Paulo  */
237e28a4053SRui Paulo struct rsn_pmksa_cache_entry *
238e28a4053SRui Paulo pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
239e28a4053SRui Paulo 		     const u8 *pmk, size_t pmk_len,
240e28a4053SRui Paulo 		const u8 *aa, const u8 *spa, int session_timeout,
241e28a4053SRui Paulo 		struct eapol_state_machine *eapol, int akmp)
242e28a4053SRui Paulo {
243e28a4053SRui Paulo 	struct rsn_pmksa_cache_entry *entry, *pos;
244e28a4053SRui Paulo 	struct os_time now;
245e28a4053SRui Paulo 
246e28a4053SRui Paulo 	if (pmk_len > PMK_LEN)
247e28a4053SRui Paulo 		return NULL;
248e28a4053SRui Paulo 
249e28a4053SRui Paulo 	entry = os_zalloc(sizeof(*entry));
250e28a4053SRui Paulo 	if (entry == NULL)
251e28a4053SRui Paulo 		return NULL;
252e28a4053SRui Paulo 	os_memcpy(entry->pmk, pmk, pmk_len);
253e28a4053SRui Paulo 	entry->pmk_len = pmk_len;
254e28a4053SRui Paulo 	rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
255e28a4053SRui Paulo 		  wpa_key_mgmt_sha256(akmp));
256e28a4053SRui Paulo 	os_get_time(&now);
257e28a4053SRui Paulo 	entry->expiration = now.sec;
258e28a4053SRui Paulo 	if (session_timeout > 0)
259e28a4053SRui Paulo 		entry->expiration += session_timeout;
260e28a4053SRui Paulo 	else
261e28a4053SRui Paulo 		entry->expiration += dot11RSNAConfigPMKLifetime;
262e28a4053SRui Paulo 	entry->akmp = akmp;
263e28a4053SRui Paulo 	os_memcpy(entry->spa, spa, ETH_ALEN);
264e28a4053SRui Paulo 	pmksa_cache_from_eapol_data(entry, eapol);
265e28a4053SRui Paulo 
266e28a4053SRui Paulo 	/* Replace an old entry for the same STA (if found) with the new entry
267e28a4053SRui Paulo 	 */
268e28a4053SRui Paulo 	pos = pmksa_cache_auth_get(pmksa, spa, NULL);
269e28a4053SRui Paulo 	if (pos)
270e28a4053SRui Paulo 		pmksa_cache_free_entry(pmksa, pos);
271e28a4053SRui Paulo 
272e28a4053SRui Paulo 	if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
273e28a4053SRui Paulo 		/* Remove the oldest entry to make room for the new entry */
274e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
275e28a4053SRui Paulo 			   "entry (for " MACSTR ") to make room for new one",
276e28a4053SRui Paulo 			   MAC2STR(pmksa->pmksa->spa));
277e28a4053SRui Paulo 		pmksa_cache_free_entry(pmksa, pmksa->pmksa);
278e28a4053SRui Paulo 	}
279e28a4053SRui Paulo 
280e28a4053SRui Paulo 	pmksa_cache_link_entry(pmksa, entry);
281e28a4053SRui Paulo 
282e28a4053SRui Paulo 	return entry;
283e28a4053SRui Paulo }
284e28a4053SRui Paulo 
285e28a4053SRui Paulo 
286e28a4053SRui Paulo struct rsn_pmksa_cache_entry *
287e28a4053SRui Paulo pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
288e28a4053SRui Paulo 		    const struct rsn_pmksa_cache_entry *old_entry,
289e28a4053SRui Paulo 		    const u8 *aa, const u8 *pmkid)
290e28a4053SRui Paulo {
291e28a4053SRui Paulo 	struct rsn_pmksa_cache_entry *entry;
292e28a4053SRui Paulo 
293e28a4053SRui Paulo 	entry = os_zalloc(sizeof(*entry));
294e28a4053SRui Paulo 	if (entry == NULL)
295e28a4053SRui Paulo 		return NULL;
296e28a4053SRui Paulo 	os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
297e28a4053SRui Paulo 	os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len);
298e28a4053SRui Paulo 	entry->pmk_len = old_entry->pmk_len;
299e28a4053SRui Paulo 	entry->expiration = old_entry->expiration;
300e28a4053SRui Paulo 	entry->akmp = old_entry->akmp;
301e28a4053SRui Paulo 	os_memcpy(entry->spa, old_entry->spa, ETH_ALEN);
302e28a4053SRui Paulo 	entry->opportunistic = 1;
303e28a4053SRui Paulo 	if (old_entry->identity) {
304e28a4053SRui Paulo 		entry->identity = os_malloc(old_entry->identity_len);
305e28a4053SRui Paulo 		if (entry->identity) {
306e28a4053SRui Paulo 			entry->identity_len = old_entry->identity_len;
307e28a4053SRui Paulo 			os_memcpy(entry->identity, old_entry->identity,
308e28a4053SRui Paulo 				  old_entry->identity_len);
309e28a4053SRui Paulo 		}
310e28a4053SRui Paulo 	}
311*f05cddf9SRui Paulo 	if (old_entry->cui)
312*f05cddf9SRui Paulo 		entry->cui = wpabuf_dup(old_entry->cui);
313e28a4053SRui Paulo #ifndef CONFIG_NO_RADIUS
314e28a4053SRui Paulo 	radius_copy_class(&entry->radius_class, &old_entry->radius_class);
315e28a4053SRui Paulo #endif /* CONFIG_NO_RADIUS */
316e28a4053SRui Paulo 	entry->eap_type_authsrv = old_entry->eap_type_authsrv;
317e28a4053SRui Paulo 	entry->vlan_id = old_entry->vlan_id;
318e28a4053SRui Paulo 	entry->opportunistic = 1;
319e28a4053SRui Paulo 
320e28a4053SRui Paulo 	pmksa_cache_link_entry(pmksa, entry);
321e28a4053SRui Paulo 
322e28a4053SRui Paulo 	return entry;
323e28a4053SRui Paulo }
324e28a4053SRui Paulo 
325e28a4053SRui Paulo 
326e28a4053SRui Paulo /**
327e28a4053SRui Paulo  * pmksa_cache_auth_deinit - Free all entries in PMKSA cache
328e28a4053SRui Paulo  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
329e28a4053SRui Paulo  */
330e28a4053SRui Paulo void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
331e28a4053SRui Paulo {
332e28a4053SRui Paulo 	struct rsn_pmksa_cache_entry *entry, *prev;
333e28a4053SRui Paulo 	int i;
334e28a4053SRui Paulo 
335e28a4053SRui Paulo 	if (pmksa == NULL)
336e28a4053SRui Paulo 		return;
337e28a4053SRui Paulo 
338e28a4053SRui Paulo 	entry = pmksa->pmksa;
339e28a4053SRui Paulo 	while (entry) {
340e28a4053SRui Paulo 		prev = entry;
341e28a4053SRui Paulo 		entry = entry->next;
342e28a4053SRui Paulo 		_pmksa_cache_free_entry(prev);
343e28a4053SRui Paulo 	}
344e28a4053SRui Paulo 	eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
345e28a4053SRui Paulo 	for (i = 0; i < PMKID_HASH_SIZE; i++)
346e28a4053SRui Paulo 		pmksa->pmkid[i] = NULL;
347e28a4053SRui Paulo 	os_free(pmksa);
348e28a4053SRui Paulo }
349e28a4053SRui Paulo 
350e28a4053SRui Paulo 
351e28a4053SRui Paulo /**
352e28a4053SRui Paulo  * pmksa_cache_auth_get - Fetch a PMKSA cache entry
353e28a4053SRui Paulo  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
354e28a4053SRui Paulo  * @spa: Supplicant address or %NULL to match any
355e28a4053SRui Paulo  * @pmkid: PMKID or %NULL to match any
356e28a4053SRui Paulo  * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
357e28a4053SRui Paulo  */
358e28a4053SRui Paulo struct rsn_pmksa_cache_entry *
359e28a4053SRui Paulo pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
360e28a4053SRui Paulo 		     const u8 *spa, const u8 *pmkid)
361e28a4053SRui Paulo {
362e28a4053SRui Paulo 	struct rsn_pmksa_cache_entry *entry;
363e28a4053SRui Paulo 
364e28a4053SRui Paulo 	if (pmkid)
365e28a4053SRui Paulo 		entry = pmksa->pmkid[PMKID_HASH(pmkid)];
366e28a4053SRui Paulo 	else
367e28a4053SRui Paulo 		entry = pmksa->pmksa;
368e28a4053SRui Paulo 	while (entry) {
369e28a4053SRui Paulo 		if ((spa == NULL ||
370e28a4053SRui Paulo 		     os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
371e28a4053SRui Paulo 		    (pmkid == NULL ||
372e28a4053SRui Paulo 		     os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
373e28a4053SRui Paulo 			return entry;
374e28a4053SRui Paulo 		entry = pmkid ? entry->hnext : entry->next;
375e28a4053SRui Paulo 	}
376e28a4053SRui Paulo 	return NULL;
377e28a4053SRui Paulo }
378e28a4053SRui Paulo 
379e28a4053SRui Paulo 
380e28a4053SRui Paulo /**
381e28a4053SRui Paulo  * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC
382e28a4053SRui Paulo  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
383e28a4053SRui Paulo  * @aa: Authenticator address
384e28a4053SRui Paulo  * @spa: Supplicant address
385e28a4053SRui Paulo  * @pmkid: PMKID
386e28a4053SRui Paulo  * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
387e28a4053SRui Paulo  *
388e28a4053SRui Paulo  * Use opportunistic key caching (OKC) to find a PMK for a supplicant.
389e28a4053SRui Paulo  */
390e28a4053SRui Paulo struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
391e28a4053SRui Paulo 	struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
392e28a4053SRui Paulo 	const u8 *pmkid)
393e28a4053SRui Paulo {
394e28a4053SRui Paulo 	struct rsn_pmksa_cache_entry *entry;
395e28a4053SRui Paulo 	u8 new_pmkid[PMKID_LEN];
396e28a4053SRui Paulo 
397e28a4053SRui Paulo 	entry = pmksa->pmksa;
398e28a4053SRui Paulo 	while (entry) {
399e28a4053SRui Paulo 		if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
400e28a4053SRui Paulo 			continue;
401e28a4053SRui Paulo 		rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
402e28a4053SRui Paulo 			  wpa_key_mgmt_sha256(entry->akmp));
403e28a4053SRui Paulo 		if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
404e28a4053SRui Paulo 			return entry;
405e28a4053SRui Paulo 		entry = entry->next;
406e28a4053SRui Paulo 	}
407e28a4053SRui Paulo 	return NULL;
408e28a4053SRui Paulo }
409e28a4053SRui Paulo 
410e28a4053SRui Paulo 
411e28a4053SRui Paulo /**
412e28a4053SRui Paulo  * pmksa_cache_auth_init - Initialize PMKSA cache
413e28a4053SRui Paulo  * @free_cb: Callback function to be called when a PMKSA cache entry is freed
414e28a4053SRui Paulo  * @ctx: Context pointer for free_cb function
415e28a4053SRui Paulo  * Returns: Pointer to PMKSA cache data or %NULL on failure
416e28a4053SRui Paulo  */
417e28a4053SRui Paulo struct rsn_pmksa_cache *
418e28a4053SRui Paulo pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
419e28a4053SRui Paulo 				      void *ctx), void *ctx)
420e28a4053SRui Paulo {
421e28a4053SRui Paulo 	struct rsn_pmksa_cache *pmksa;
422e28a4053SRui Paulo 
423e28a4053SRui Paulo 	pmksa = os_zalloc(sizeof(*pmksa));
424e28a4053SRui Paulo 	if (pmksa) {
425e28a4053SRui Paulo 		pmksa->free_cb = free_cb;
426e28a4053SRui Paulo 		pmksa->ctx = ctx;
427e28a4053SRui Paulo 	}
428e28a4053SRui Paulo 
429e28a4053SRui Paulo 	return pmksa;
430e28a4053SRui Paulo }
431