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 
_pmksa_cache_free_entry(struct rsn_pmksa_cache_entry * entry)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 
pmksa_cache_free_entry(struct rsn_pmksa_cache * pmksa,struct rsn_pmksa_cache_entry * entry)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  */
pmksa_cache_auth_flush(struct rsn_pmksa_cache * pmksa)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 
pmksa_cache_expire(void * eloop_ctx,void * timeout_ctx)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 
pmksa_cache_set_expiration(struct rsn_pmksa_cache * pmksa)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 
pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry * entry,struct eapol_state_machine * eapol)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 
pmksa_cache_to_eapol_data(struct hostapd_data * hapd,struct rsn_pmksa_cache_entry * entry,struct eapol_state_machine * eapol)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 
pmksa_cache_link_entry(struct rsn_pmksa_cache * pmksa,struct rsn_pmksa_cache_entry * entry)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 *
pmksa_cache_auth_add(struct rsn_pmksa_cache * pmksa,const u8 * pmk,size_t pmk_len,const u8 * pmkid,const u8 * kck,size_t kck_len,const u8 * aa,const u8 * spa,int session_timeout,struct eapol_state_machine * eapol,int akmp)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 *
pmksa_cache_auth_create_entry(const u8 * pmk,size_t pmk_len,const u8 * pmkid,const u8 * kck,size_t kck_len,const u8 * aa,const u8 * spa,int session_timeout,struct eapol_state_machine * eapol,int akmp)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  */
pmksa_cache_auth_add_entry(struct rsn_pmksa_cache * pmksa,struct rsn_pmksa_cache_entry * entry)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 *
pmksa_cache_add_okc(struct rsn_pmksa_cache * pmksa,const struct rsn_pmksa_cache_entry * old_entry,const u8 * aa,const u8 * pmkid)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  */
pmksa_cache_auth_deinit(struct rsn_pmksa_cache * pmksa)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 *
pmksa_cache_auth_get(struct rsn_pmksa_cache * pmksa,const u8 * spa,const u8 * pmkid)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  */
pmksa_cache_get_okc(struct rsn_pmksa_cache * pmksa,const u8 * aa,const u8 * spa,const u8 * pmkid)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 *
pmksa_cache_auth_init(void (* free_cb)(struct rsn_pmksa_cache_entry * entry,void * ctx),void * ctx)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 
das_attr_match(struct rsn_pmksa_cache_entry * entry,struct radius_das_attrs * attr)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 
pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache * pmksa,struct radius_das_attrs * attr)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  */
pmksa_cache_auth_list(struct rsn_pmksa_cache * pmksa,char * buf,size_t len)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  */
pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache * pmksa,const u8 * addr,char * buf,size_t len)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