xref: /freebsd/contrib/wpa/src/ap/pmksa_cache_auth.c (revision 87b759f0fa1f7554d50ce640c40138512bbded44)
1 /*
2  * hostapd - PMKSA cache for IEEE 802.11i RSN
3  * Copyright (c) 2004-2008, 2012-2015, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #include "utils/includes.h"
10 
11 #include "utils/common.h"
12 #include "utils/eloop.h"
13 #include "eapol_auth/eapol_auth_sm.h"
14 #include "eapol_auth/eapol_auth_sm_i.h"
15 #include "radius/radius_das.h"
16 #include "sta_info.h"
17 #include "ap_config.h"
18 #include "pmksa_cache_auth.h"
19 
20 
21 static const int pmksa_cache_max_entries = 1024;
22 static const int dot11RSNAConfigPMKLifetime = 43200;
23 
24 struct rsn_pmksa_cache {
25 #define PMKID_HASH_SIZE 128
26 #define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f)
27 	struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE];
28 	struct rsn_pmksa_cache_entry *pmksa;
29 	int pmksa_count;
30 
31 	void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
32 	void *ctx;
33 };
34 
35 
36 static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
37 
38 
39 static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
40 {
41 	os_free(entry->vlan_desc);
42 	os_free(entry->identity);
43 	os_free(entry->dpp_pkhash);
44 	wpabuf_free(entry->cui);
45 #ifndef CONFIG_NO_RADIUS
46 	radius_free_class(&entry->radius_class);
47 #endif /* CONFIG_NO_RADIUS */
48 	bin_clear_free(entry, sizeof(*entry));
49 }
50 
51 
52 void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
53 			    struct rsn_pmksa_cache_entry *entry)
54 {
55 	struct rsn_pmksa_cache_entry *pos, *prev;
56 	unsigned int hash;
57 
58 	pmksa->pmksa_count--;
59 
60 	if (pmksa->free_cb)
61 		pmksa->free_cb(entry, pmksa->ctx);
62 
63 	/* unlink from hash list */
64 	hash = PMKID_HASH(entry->pmkid);
65 	pos = pmksa->pmkid[hash];
66 	prev = NULL;
67 	while (pos) {
68 		if (pos == entry) {
69 			if (prev != NULL)
70 				prev->hnext = entry->hnext;
71 			else
72 				pmksa->pmkid[hash] = entry->hnext;
73 			break;
74 		}
75 		prev = pos;
76 		pos = pos->hnext;
77 	}
78 
79 	/* unlink from entry list */
80 	pos = pmksa->pmksa;
81 	prev = NULL;
82 	while (pos) {
83 		if (pos == entry) {
84 			if (prev != NULL)
85 				prev->next = entry->next;
86 			else
87 				pmksa->pmksa = entry->next;
88 			break;
89 		}
90 		prev = pos;
91 		pos = pos->next;
92 	}
93 
94 	_pmksa_cache_free_entry(entry);
95 }
96 
97 
98 /**
99  * pmksa_cache_auth_flush - Flush all PMKSA cache entries
100  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
101  */
102 void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa)
103 {
104 	while (pmksa->pmksa) {
105 		wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry for "
106 			   MACSTR, MAC2STR(pmksa->pmksa->spa));
107 		pmksa_cache_free_entry(pmksa, pmksa->pmksa);
108 	}
109 }
110 
111 
112 static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
113 {
114 	struct rsn_pmksa_cache *pmksa = eloop_ctx;
115 	struct os_reltime now;
116 
117 	os_get_reltime(&now);
118 	while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
119 		wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
120 			   MACSTR, MAC2STR(pmksa->pmksa->spa));
121 		pmksa_cache_free_entry(pmksa, pmksa->pmksa);
122 	}
123 
124 	pmksa_cache_set_expiration(pmksa);
125 }
126 
127 
128 static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
129 {
130 	int sec;
131 	struct os_reltime now;
132 
133 	eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
134 	if (pmksa->pmksa == NULL)
135 		return;
136 	os_get_reltime(&now);
137 	sec = pmksa->pmksa->expiration - now.sec;
138 	if (sec < 0)
139 		sec = 0;
140 	eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
141 }
142 
143 
144 static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
145 					struct eapol_state_machine *eapol)
146 {
147 	struct vlan_description *vlan_desc;
148 
149 	if (eapol == NULL)
150 		return;
151 
152 	if (eapol->identity) {
153 		entry->identity = os_malloc(eapol->identity_len);
154 		if (entry->identity) {
155 			entry->identity_len = eapol->identity_len;
156 			os_memcpy(entry->identity, eapol->identity,
157 				  eapol->identity_len);
158 		}
159 	}
160 
161 	if (eapol->radius_cui)
162 		entry->cui = wpabuf_dup(eapol->radius_cui);
163 
164 #ifndef CONFIG_NO_RADIUS
165 	radius_copy_class(&entry->radius_class, &eapol->radius_class);
166 #endif /* CONFIG_NO_RADIUS */
167 
168 	entry->eap_type_authsrv = eapol->eap_type_authsrv;
169 
170 	vlan_desc = ((struct sta_info *) eapol->sta)->vlan_desc;
171 	if (vlan_desc && vlan_desc->notempty) {
172 		entry->vlan_desc = os_zalloc(sizeof(struct vlan_description));
173 		if (entry->vlan_desc)
174 			*entry->vlan_desc = *vlan_desc;
175 	} else {
176 		entry->vlan_desc = NULL;
177 	}
178 
179 	entry->acct_multi_session_id = eapol->acct_multi_session_id;
180 }
181 
182 
183 void pmksa_cache_to_eapol_data(struct hostapd_data *hapd,
184 			       struct rsn_pmksa_cache_entry *entry,
185 			       struct eapol_state_machine *eapol)
186 {
187 	if (entry == NULL || eapol == NULL)
188 		return;
189 
190 	if (entry->identity) {
191 		os_free(eapol->identity);
192 		eapol->identity = os_malloc(entry->identity_len);
193 		if (eapol->identity) {
194 			eapol->identity_len = entry->identity_len;
195 			os_memcpy(eapol->identity, entry->identity,
196 				  entry->identity_len);
197 		}
198 		wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA",
199 				  eapol->identity, eapol->identity_len);
200 	}
201 
202 	if (entry->cui) {
203 		wpabuf_free(eapol->radius_cui);
204 		eapol->radius_cui = wpabuf_dup(entry->cui);
205 	}
206 
207 #ifndef CONFIG_NO_RADIUS
208 	radius_free_class(&eapol->radius_class);
209 	radius_copy_class(&eapol->radius_class, &entry->radius_class);
210 #endif /* CONFIG_NO_RADIUS */
211 	if (eapol->radius_class.attr) {
212 		wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from "
213 			   "PMKSA", (unsigned long) eapol->radius_class.count);
214 	}
215 
216 	eapol->eap_type_authsrv = entry->eap_type_authsrv;
217 #ifndef CONFIG_NO_VLAN
218 	ap_sta_set_vlan(hapd, eapol->sta, entry->vlan_desc);
219 #endif /* CONFIG_NO_VLAN */
220 
221 	eapol->acct_multi_session_id = entry->acct_multi_session_id;
222 }
223 
224 
225 static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
226 				   struct rsn_pmksa_cache_entry *entry)
227 {
228 	struct rsn_pmksa_cache_entry *pos, *prev;
229 	int hash;
230 
231 	/* Add the new entry; order by expiration time */
232 	pos = pmksa->pmksa;
233 	prev = NULL;
234 	while (pos) {
235 		if (pos->expiration > entry->expiration)
236 			break;
237 		prev = pos;
238 		pos = pos->next;
239 	}
240 	if (prev == NULL) {
241 		entry->next = pmksa->pmksa;
242 		pmksa->pmksa = entry;
243 	} else {
244 		entry->next = prev->next;
245 		prev->next = entry;
246 	}
247 
248 	hash = PMKID_HASH(entry->pmkid);
249 	entry->hnext = pmksa->pmkid[hash];
250 	pmksa->pmkid[hash] = entry;
251 
252 	pmksa->pmksa_count++;
253 	if (prev == NULL)
254 		pmksa_cache_set_expiration(pmksa);
255 	wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
256 		   MAC2STR(entry->spa));
257 	wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
258 }
259 
260 
261 /**
262  * pmksa_cache_auth_add - Add a PMKSA cache entry
263  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
264  * @pmk: The new pairwise master key
265  * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
266  * @pmkid: Calculated PMKID
267  * @kck: Key confirmation key or %NULL if not yet derived
268  * @kck_len: KCK length in bytes
269  * @aa: Authenticator address
270  * @spa: Supplicant address
271  * @session_timeout: Session timeout
272  * @eapol: Pointer to EAPOL state machine data
273  * @akmp: WPA_KEY_MGMT_* used in key derivation
274  * Returns: Pointer to the added PMKSA cache entry or %NULL on error
275  *
276  * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
277  * cache. If an old entry is already in the cache for the same Supplicant,
278  * this entry will be replaced with the new entry. PMKID will be calculated
279  * based on the PMK.
280  */
281 struct rsn_pmksa_cache_entry *
282 pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
283 		     const u8 *pmk, size_t pmk_len, const u8 *pmkid,
284 		     const u8 *kck, size_t kck_len,
285 		     const u8 *aa, const u8 *spa, int session_timeout,
286 		     struct eapol_state_machine *eapol, int akmp)
287 {
288 	struct rsn_pmksa_cache_entry *entry;
289 
290 	entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len,
291 					      aa, spa, session_timeout, eapol,
292 					      akmp);
293 
294 	if (pmksa_cache_auth_add_entry(pmksa, entry) < 0)
295 		return NULL;
296 
297 	return entry;
298 }
299 
300 
301 /**
302  * pmksa_cache_auth_create_entry - Create a PMKSA cache entry
303  * @pmk: The new pairwise master key
304  * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
305  * @pmkid: Calculated PMKID
306  * @kck: Key confirmation key or %NULL if not yet derived
307  * @kck_len: KCK length in bytes
308  * @aa: Authenticator address
309  * @spa: Supplicant address
310  * @session_timeout: Session timeout
311  * @eapol: Pointer to EAPOL state machine data
312  * @akmp: WPA_KEY_MGMT_* used in key derivation
313  * Returns: Pointer to the added PMKSA cache entry or %NULL on error
314  *
315  * This function creates a PMKSA entry.
316  */
317 struct rsn_pmksa_cache_entry *
318 pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
319 			      const u8 *kck, size_t kck_len, const u8 *aa,
320 			      const u8 *spa, int session_timeout,
321 			      struct eapol_state_machine *eapol, int akmp)
322 {
323 	struct rsn_pmksa_cache_entry *entry;
324 	struct os_reltime now;
325 
326 	if (pmk_len > PMK_LEN_MAX)
327 		return NULL;
328 
329 	if (wpa_key_mgmt_suite_b(akmp) && !kck)
330 		return NULL;
331 
332 	entry = os_zalloc(sizeof(*entry));
333 	if (entry == NULL)
334 		return NULL;
335 	os_memcpy(entry->pmk, pmk, pmk_len);
336 	entry->pmk_len = pmk_len;
337 	if (kck && kck_len && kck_len < WPA_KCK_MAX_LEN) {
338 		os_memcpy(entry->kck, kck, kck_len);
339 		entry->kck_len = kck_len;
340 	}
341 	if (pmkid)
342 		os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
343 	else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
344 		rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
345 	else if (wpa_key_mgmt_suite_b(akmp))
346 		rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
347 	else
348 		rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
349 	os_get_reltime(&now);
350 	entry->expiration = now.sec;
351 	if (session_timeout > 0)
352 		entry->expiration += session_timeout;
353 	else
354 		entry->expiration += dot11RSNAConfigPMKLifetime;
355 	entry->akmp = akmp;
356 	os_memcpy(entry->spa, spa, ETH_ALEN);
357 	pmksa_cache_from_eapol_data(entry, eapol);
358 
359 	return entry;
360 }
361 
362 
363 /**
364  * pmksa_cache_auth_add_entry - Add a PMKSA cache entry
365  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
366  * @entry: Pointer to PMKSA cache entry
367  *
368  * This function adds PMKSA cache entry to the PMKSA cache. If an old entry is
369  * already in the cache for the same Supplicant, this entry will be replaced
370  * with the new entry. PMKID will be calculated based on the PMK.
371  */
372 int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
373 			       struct rsn_pmksa_cache_entry *entry)
374 {
375 	struct rsn_pmksa_cache_entry *pos;
376 
377 	if (entry == NULL)
378 		return -1;
379 
380 	/* Replace an old entry for the same STA (if found) with the new entry
381 	 */
382 	pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL);
383 	if (pos)
384 		pmksa_cache_free_entry(pmksa, pos);
385 
386 	if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
387 		/* Remove the oldest entry to make room for the new entry */
388 		wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
389 			   "entry (for " MACSTR ") to make room for new one",
390 			   MAC2STR(pmksa->pmksa->spa));
391 		pmksa_cache_free_entry(pmksa, pmksa->pmksa);
392 	}
393 
394 	pmksa_cache_link_entry(pmksa, entry);
395 
396 	return 0;
397 }
398 
399 
400 struct rsn_pmksa_cache_entry *
401 pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
402 		    const struct rsn_pmksa_cache_entry *old_entry,
403 		    const u8 *aa, const u8 *pmkid)
404 {
405 	struct rsn_pmksa_cache_entry *entry;
406 
407 	entry = os_zalloc(sizeof(*entry));
408 	if (entry == NULL)
409 		return NULL;
410 	os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
411 	os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len);
412 	entry->pmk_len = old_entry->pmk_len;
413 	entry->expiration = old_entry->expiration;
414 	entry->akmp = old_entry->akmp;
415 	os_memcpy(entry->spa, old_entry->spa, ETH_ALEN);
416 	entry->opportunistic = 1;
417 	if (old_entry->identity) {
418 		entry->identity = os_malloc(old_entry->identity_len);
419 		if (entry->identity) {
420 			entry->identity_len = old_entry->identity_len;
421 			os_memcpy(entry->identity, old_entry->identity,
422 				  old_entry->identity_len);
423 		}
424 	}
425 	if (old_entry->cui)
426 		entry->cui = wpabuf_dup(old_entry->cui);
427 #ifndef CONFIG_NO_RADIUS
428 	radius_copy_class(&entry->radius_class, &old_entry->radius_class);
429 #endif /* CONFIG_NO_RADIUS */
430 	entry->eap_type_authsrv = old_entry->eap_type_authsrv;
431 	if (old_entry->vlan_desc) {
432 		entry->vlan_desc = os_zalloc(sizeof(struct vlan_description));
433 		if (entry->vlan_desc)
434 			*entry->vlan_desc = *old_entry->vlan_desc;
435 	} else {
436 		entry->vlan_desc = NULL;
437 	}
438 	entry->opportunistic = 1;
439 
440 	pmksa_cache_link_entry(pmksa, entry);
441 
442 	return entry;
443 }
444 
445 
446 /**
447  * pmksa_cache_auth_deinit - Free all entries in PMKSA cache
448  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
449  */
450 void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
451 {
452 	struct rsn_pmksa_cache_entry *entry, *prev;
453 	int i;
454 
455 	if (pmksa == NULL)
456 		return;
457 
458 	entry = pmksa->pmksa;
459 	while (entry) {
460 		prev = entry;
461 		entry = entry->next;
462 		_pmksa_cache_free_entry(prev);
463 	}
464 	eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
465 	pmksa->pmksa_count = 0;
466 	pmksa->pmksa = NULL;
467 	for (i = 0; i < PMKID_HASH_SIZE; i++)
468 		pmksa->pmkid[i] = NULL;
469 	os_free(pmksa);
470 }
471 
472 
473 /**
474  * pmksa_cache_auth_get - Fetch a PMKSA cache entry
475  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
476  * @spa: Supplicant address or %NULL to match any
477  * @pmkid: PMKID or %NULL to match any
478  * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
479  */
480 struct rsn_pmksa_cache_entry *
481 pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
482 		     const u8 *spa, const u8 *pmkid)
483 {
484 	struct rsn_pmksa_cache_entry *entry;
485 
486 	if (pmkid) {
487 		for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry;
488 		     entry = entry->hnext) {
489 			if ((spa == NULL ||
490 			     ether_addr_equal(entry->spa, spa)) &&
491 			    os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
492 				return entry;
493 		}
494 	} else {
495 		for (entry = pmksa->pmksa; entry; entry = entry->next) {
496 			if (spa == NULL ||
497 			    ether_addr_equal(entry->spa, spa))
498 				return entry;
499 		}
500 	}
501 
502 	return NULL;
503 }
504 
505 
506 /**
507  * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC
508  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
509  * @aa: Authenticator address
510  * @spa: Supplicant address
511  * @pmkid: PMKID
512  * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
513  *
514  * Use opportunistic key caching (OKC) to find a PMK for a supplicant.
515  */
516 struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
517 	struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
518 	const u8 *pmkid)
519 {
520 	struct rsn_pmksa_cache_entry *entry;
521 	u8 new_pmkid[PMKID_LEN];
522 
523 	for (entry = pmksa->pmksa; entry; entry = entry->next) {
524 		if (!ether_addr_equal(entry->spa, spa))
525 			continue;
526 		if (wpa_key_mgmt_sae(entry->akmp) ||
527 		    wpa_key_mgmt_fils(entry->akmp)) {
528 			if (os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
529 				return entry;
530 			continue;
531 		}
532 		if (entry->akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 &&
533 		    entry->kck_len > 0)
534 			rsn_pmkid_suite_b_192(entry->kck, entry->kck_len,
535 					      aa, spa, new_pmkid);
536 		else if (wpa_key_mgmt_suite_b(entry->akmp) &&
537 			 entry->kck_len > 0)
538 		rsn_pmkid_suite_b(entry->kck, entry->kck_len, aa, spa,
539 				  new_pmkid);
540 		else
541 			rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa,
542 				  new_pmkid, entry->akmp);
543 		if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
544 			return entry;
545 	}
546 	return NULL;
547 }
548 
549 
550 /**
551  * pmksa_cache_auth_init - Initialize PMKSA cache
552  * @free_cb: Callback function to be called when a PMKSA cache entry is freed
553  * @ctx: Context pointer for free_cb function
554  * Returns: Pointer to PMKSA cache data or %NULL on failure
555  */
556 struct rsn_pmksa_cache *
557 pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
558 				      void *ctx), void *ctx)
559 {
560 	struct rsn_pmksa_cache *pmksa;
561 
562 	pmksa = os_zalloc(sizeof(*pmksa));
563 	if (pmksa) {
564 		pmksa->free_cb = free_cb;
565 		pmksa->ctx = ctx;
566 	}
567 
568 	return pmksa;
569 }
570 
571 
572 static int das_attr_match(struct rsn_pmksa_cache_entry *entry,
573 			  struct radius_das_attrs *attr)
574 {
575 	int match = 0;
576 
577 	if (attr->sta_addr) {
578 		if (!ether_addr_equal(attr->sta_addr, entry->spa))
579 			return 0;
580 		match++;
581 	}
582 
583 	if (attr->acct_multi_session_id) {
584 		char buf[20];
585 
586 		if (attr->acct_multi_session_id_len != 16)
587 			return 0;
588 		os_snprintf(buf, sizeof(buf), "%016llX",
589 			    (unsigned long long) entry->acct_multi_session_id);
590 		if (os_memcmp(attr->acct_multi_session_id, buf, 16) != 0)
591 			return 0;
592 		match++;
593 	}
594 
595 	if (attr->cui) {
596 		if (!entry->cui ||
597 		    attr->cui_len != wpabuf_len(entry->cui) ||
598 		    os_memcmp(attr->cui, wpabuf_head(entry->cui),
599 			      attr->cui_len) != 0)
600 			return 0;
601 		match++;
602 	}
603 
604 	if (attr->user_name) {
605 		if (!entry->identity ||
606 		    attr->user_name_len != entry->identity_len ||
607 		    os_memcmp(attr->user_name, entry->identity,
608 			      attr->user_name_len) != 0)
609 			return 0;
610 		match++;
611 	}
612 
613 	return match;
614 }
615 
616 
617 int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
618 					   struct radius_das_attrs *attr)
619 {
620 	int found = 0;
621 	struct rsn_pmksa_cache_entry *entry, *prev;
622 
623 	if (attr->acct_session_id)
624 		return -1;
625 
626 	entry = pmksa->pmksa;
627 	while (entry) {
628 		if (das_attr_match(entry, attr)) {
629 			found++;
630 			prev = entry;
631 			entry = entry->next;
632 			pmksa_cache_free_entry(pmksa, prev);
633 			continue;
634 		}
635 		entry = entry->next;
636 	}
637 
638 	return found ? 0 : -1;
639 }
640 
641 
642 /**
643  * pmksa_cache_auth_list - Dump text list of entries in PMKSA cache
644  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
645  * @buf: Buffer for the list
646  * @len: Length of the buffer
647  * Returns: Number of bytes written to buffer
648  *
649  * This function is used to generate a text format representation of the
650  * current PMKSA cache contents for the ctrl_iface PMKSA command.
651  */
652 int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
653 {
654 	int i, ret;
655 	char *pos = buf;
656 	struct rsn_pmksa_cache_entry *entry;
657 	struct os_reltime now;
658 
659 	os_get_reltime(&now);
660 	ret = os_snprintf(pos, buf + len - pos,
661 			  "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
662 	if (os_snprintf_error(buf + len - pos, ret))
663 		return pos - buf;
664 	pos += ret;
665 	i = 0;
666 	entry = pmksa->pmksa;
667 	while (entry) {
668 		ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
669 				  i, MAC2STR(entry->spa));
670 		if (os_snprintf_error(buf + len - pos, ret))
671 			return pos - buf;
672 		pos += ret;
673 		pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
674 					PMKID_LEN);
675 		ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
676 				  (int) (entry->expiration - now.sec),
677 				  entry->opportunistic);
678 		if (os_snprintf_error(buf + len - pos, ret))
679 			return pos - buf;
680 		pos += ret;
681 		entry = entry->next;
682 	}
683 	return pos - buf;
684 }
685 
686 
687 #ifdef CONFIG_PMKSA_CACHE_EXTERNAL
688 #ifdef CONFIG_MESH
689 
690 /**
691  * pmksa_cache_auth_list_mesh - Dump text list of entries in PMKSA cache
692  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
693  * @addr: MAC address of the peer (NULL means any)
694  * @buf: Buffer for the list
695  * @len: Length of the buffer
696  * Returns: Number of bytes written to buffer
697  *
698  * This function is used to generate a text format representation of the
699  * current PMKSA cache contents for the ctrl_iface PMKSA_GET command to store
700  * in external storage.
701  */
702 int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
703 			       char *buf, size_t len)
704 {
705 	int ret;
706 	char *pos, *end;
707 	struct rsn_pmksa_cache_entry *entry;
708 	struct os_reltime now;
709 
710 	pos = buf;
711 	end = buf + len;
712 	os_get_reltime(&now);
713 
714 
715 	/*
716 	 * Entry format:
717 	 * <BSSID> <PMKID> <PMK> <expiration in seconds>
718 	 */
719 	for (entry = pmksa->pmksa; entry; entry = entry->next) {
720 		if (addr && !ether_addr_equal(entry->spa, addr))
721 			continue;
722 
723 		ret = os_snprintf(pos, end - pos, MACSTR " ",
724 				  MAC2STR(entry->spa));
725 		if (os_snprintf_error(end - pos, ret))
726 			return 0;
727 		pos += ret;
728 
729 		pos += wpa_snprintf_hex(pos, end - pos, entry->pmkid,
730 					PMKID_LEN);
731 
732 		ret = os_snprintf(pos, end - pos, " ");
733 		if (os_snprintf_error(end - pos, ret))
734 			return 0;
735 		pos += ret;
736 
737 		pos += wpa_snprintf_hex(pos, end - pos, entry->pmk,
738 					entry->pmk_len);
739 
740 		ret = os_snprintf(pos, end - pos, " %d\n",
741 				  (int) (entry->expiration - now.sec));
742 		if (os_snprintf_error(end - pos, ret))
743 			return 0;
744 		pos += ret;
745 	}
746 
747 	return pos - buf;
748 }
749 
750 #endif /* CONFIG_MESH */
751 #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
752