Home | History | Annotate | Line # | Download | only in ap
      1 /*
      2  * hostapd - PMKSA cache for IEEE 802.11i RSN
      3  * Copyright (c) 2004-2008, 2012-2015, Jouni Malinen <j (at) 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