1 /*
2 * WPA Supplicant - RSN PMKSA cache
3 * Copyright (c) 2004-2009, 2011-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 "includes.h"
10
11 #include "common.h"
12 #include "eloop.h"
13 #include "eapol_supp/eapol_supp_sm.h"
14 #include "wpa.h"
15 #include "wpa_i.h"
16 #include "pmksa_cache.h"
17
18 #if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA)
19
20 static const int pmksa_cache_max_entries = 32;
21
22 struct rsn_pmksa_cache {
23 struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */
24 int pmksa_count; /* number of entries in PMKSA cache */
25 struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
26
27 void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
28 enum pmksa_free_reason reason);
29 bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
30 void *ctx);
31 void *ctx;
32 };
33
34
35 static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
36
37
_pmksa_cache_free_entry(struct rsn_pmksa_cache_entry * entry)38 static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
39 {
40 bin_clear_free(entry, sizeof(*entry));
41 }
42
43
pmksa_cache_free_entry(struct rsn_pmksa_cache * pmksa,struct rsn_pmksa_cache_entry * entry,enum pmksa_free_reason reason)44 static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
45 struct rsn_pmksa_cache_entry *entry,
46 enum pmksa_free_reason reason)
47 {
48 wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
49 entry->pmkid,
50 entry->fils_cache_id_set ? entry->fils_cache_id :
51 NULL);
52 pmksa->pmksa_count--;
53 pmksa->free_cb(entry, pmksa->ctx, reason);
54 _pmksa_cache_free_entry(entry);
55 }
56
57
pmksa_cache_expire(void * eloop_ctx,void * timeout_ctx)58 static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
59 {
60 struct rsn_pmksa_cache *pmksa = eloop_ctx;
61 struct os_reltime now;
62 struct rsn_pmksa_cache_entry *prev = NULL, *tmp;
63 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
64
65 os_get_reltime(&now);
66 while (entry && entry->expiration <= now.sec) {
67 if (wpa_key_mgmt_sae(entry->akmp) &&
68 pmksa->is_current_cb(entry, pmksa->ctx)) {
69 /* Do not expire the currently used PMKSA entry for SAE
70 * since there is no convenient mechanism for
71 * reauthenticating during an association with SAE. The
72 * expired entry will be removed after this association
73 * has been lost. */
74 wpa_printf(MSG_DEBUG,
75 "RSN: postpone PMKSA cache entry expiration for SAE with "
76 MACSTR, MAC2STR(entry->aa));
77 prev = entry;
78 entry = entry->next;
79 continue;
80 }
81
82 wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
83 MACSTR, MAC2STR(entry->aa));
84 if (prev)
85 prev->next = entry->next;
86 else
87 pmksa->pmksa = entry->next;
88 tmp = entry;
89 entry = entry->next;
90 pmksa_cache_free_entry(pmksa, tmp, PMKSA_EXPIRE);
91 }
92
93 pmksa_cache_set_expiration(pmksa);
94 }
95
96
pmksa_cache_reauth(void * eloop_ctx,void * timeout_ctx)97 static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
98 {
99 struct rsn_pmksa_cache *pmksa = eloop_ctx;
100 pmksa->sm->cur_pmksa = NULL;
101 eapol_sm_request_reauth(pmksa->sm->eapol);
102 }
103
104
pmksa_cache_set_expiration(struct rsn_pmksa_cache * pmksa)105 static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
106 {
107 int sec;
108 struct rsn_pmksa_cache_entry *entry;
109 struct os_reltime now;
110
111 eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
112 eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL);
113 if (pmksa->pmksa == NULL)
114 return;
115 os_get_reltime(&now);
116 sec = pmksa->pmksa->expiration - now.sec;
117 if (sec < 0) {
118 sec = 0;
119 if (wpa_key_mgmt_sae(pmksa->pmksa->akmp) &&
120 pmksa->is_current_cb(pmksa->pmksa, pmksa->ctx)) {
121 /* Do not continue polling for the current PMKSA entry
122 * from SAE to expire every second. Use the expiration
123 * time to the following entry, if any, and wait at
124 * maximum 10 minutes to check again.
125 */
126 entry = pmksa->pmksa->next;
127 if (entry) {
128 sec = entry->expiration - now.sec;
129 if (sec < 0)
130 sec = 0;
131 else if (sec > 600)
132 sec = 600;
133 } else {
134 sec = 600;
135 }
136 }
137 }
138 eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
139
140 entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
141 pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, 0);
142 if (entry && !wpa_key_mgmt_sae(entry->akmp)) {
143 sec = pmksa->pmksa->reauth_time - now.sec;
144 if (sec < 0)
145 sec = 0;
146 eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa,
147 NULL);
148 }
149 }
150
151
152 /**
153 * pmksa_cache_add - Add a PMKSA cache entry
154 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
155 * @pmk: The new pairwise master key
156 * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
157 * @pmkid: Calculated PMKID
158 * @kck: Key confirmation key or %NULL if not yet derived
159 * @kck_len: KCK length in bytes
160 * @aa: Authenticator address
161 * @spa: Supplicant address
162 * @network_ctx: Network configuration context for this PMK
163 * @akmp: WPA_KEY_MGMT_* used in key derivation
164 * @cache_id: Pointer to FILS Cache Identifier or %NULL if not advertised
165 * Returns: Pointer to the added PMKSA cache entry or %NULL on error
166 *
167 * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
168 * cache. If an old entry is already in the cache for the same Authenticator,
169 * this entry will be replaced with the new entry. PMKID will be calculated
170 * based on the PMK and the driver interface is notified of the new PMKID.
171 */
172 struct rsn_pmksa_cache_entry *
pmksa_cache_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,void * network_ctx,int akmp,const u8 * cache_id)173 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
174 const u8 *pmkid, const u8 *kck, size_t kck_len,
175 const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
176 const u8 *cache_id)
177 {
178 struct rsn_pmksa_cache_entry *entry;
179 struct os_reltime now;
180
181 if (pmk_len > PMK_LEN_MAX)
182 return NULL;
183
184 #ifdef CONFIG_SUITEB
185 if (wpa_key_mgmt_suite_b(akmp) && !kck)
186 return NULL;
187 #endif /* CONFIG_SUITEB */
188
189 entry = os_zalloc(sizeof(*entry));
190 if (entry == NULL)
191 return NULL;
192 os_memcpy(entry->pmk, pmk, pmk_len);
193 entry->pmk_len = pmk_len;
194 if (pmkid)
195 os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
196 else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
197 rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
198 #ifdef CONFIG_SUITEB
199 else if (wpa_key_mgmt_suite_b(akmp))
200 rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
201 #endif /* CONFIG_SUITEB */
202 else
203 rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
204 os_get_reltime(&now);
205 entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
206 entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
207 pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
208 entry->akmp = akmp;
209 if (cache_id) {
210 entry->fils_cache_id_set = 1;
211 os_memcpy(entry->fils_cache_id, cache_id, FILS_CACHE_ID_LEN);
212 }
213 os_memcpy(entry->aa, aa, ETH_ALEN);
214 entry->network_ctx = network_ctx;
215
216 return pmksa_cache_add_entry(pmksa, entry);
217 }
218
219
220 struct rsn_pmksa_cache_entry *
pmksa_cache_add_entry(struct rsn_pmksa_cache * pmksa,struct rsn_pmksa_cache_entry * entry)221 pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
222 struct rsn_pmksa_cache_entry *entry)
223 {
224 struct rsn_pmksa_cache_entry *pos, *prev;
225
226 /* Replace an old entry for the same Authenticator (if found) with the
227 * new entry */
228 pos = pmksa->pmksa;
229 prev = NULL;
230 while (pos) {
231 if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0) {
232 if (pos->pmk_len == entry->pmk_len &&
233 os_memcmp_const(pos->pmk, entry->pmk,
234 entry->pmk_len) == 0 &&
235 os_memcmp_const(pos->pmkid, entry->pmkid,
236 PMKID_LEN) == 0) {
237 wpa_printf(MSG_DEBUG, "WPA: reusing previous "
238 "PMKSA entry");
239 os_free(entry);
240 return pos;
241 }
242 if (prev == NULL)
243 pmksa->pmksa = pos->next;
244 else
245 prev->next = pos->next;
246
247 /*
248 * If OKC is used, there may be other PMKSA cache
249 * entries based on the same PMK. These needs to be
250 * flushed so that a new entry can be created based on
251 * the new PMK. Only clear other entries if they have a
252 * matching PMK and this PMK has been used successfully
253 * with the current AP, i.e., if opportunistic flag has
254 * been cleared in wpa_supplicant_key_neg_complete().
255 */
256 wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
257 "the current AP and any PMKSA cache entry "
258 "that was based on the old PMK");
259 if (!pos->opportunistic)
260 pmksa_cache_flush(pmksa, entry->network_ctx,
261 pos->pmk, pos->pmk_len,
262 false);
263 pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE);
264 break;
265 }
266 prev = pos;
267 pos = pos->next;
268 }
269
270 if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
271 /* Remove the oldest entry to make room for the new entry */
272 pos = pmksa->pmksa;
273
274 if (pos == pmksa->sm->cur_pmksa) {
275 /*
276 * Never remove the current PMKSA cache entry, since
277 * it's in use, and removing it triggers a needless
278 * deauthentication.
279 */
280 pos = pos->next;
281 pmksa->pmksa->next = pos ? pos->next : NULL;
282 } else
283 pmksa->pmksa = pos->next;
284
285 if (pos) {
286 wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle "
287 "PMKSA cache entry (for " MACSTR ") to "
288 "make room for new one",
289 MAC2STR(pos->aa));
290 pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE);
291 }
292 }
293
294 /* Add the new entry; order by expiration time */
295 pos = pmksa->pmksa;
296 prev = NULL;
297 while (pos) {
298 if (pos->expiration > entry->expiration)
299 break;
300 prev = pos;
301 pos = pos->next;
302 }
303 if (prev == NULL) {
304 entry->next = pmksa->pmksa;
305 pmksa->pmksa = entry;
306 pmksa_cache_set_expiration(pmksa);
307 } else {
308 entry->next = prev->next;
309 prev->next = entry;
310 }
311 pmksa->pmksa_count++;
312 wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
313 " network_ctx=%p akmp=0x%x", MAC2STR(entry->aa),
314 entry->network_ctx, entry->akmp);
315 wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid,
316 entry->fils_cache_id_set ? entry->fils_cache_id : NULL,
317 entry->pmk, entry->pmk_len,
318 pmksa->sm->dot11RSNAConfigPMKLifetime,
319 pmksa->sm->dot11RSNAConfigPMKReauthThreshold,
320 entry->akmp);
321
322 return entry;
323 }
324
325
326 /**
327 * pmksa_cache_flush - Flush PMKSA cache entries for a specific network
328 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
329 * @network_ctx: Network configuration context or %NULL to flush all entries
330 * @pmk: PMK to match for or %NULL to match all PMKs
331 * @pmk_len: PMK length
332 * @external_only: Flush only PMKSA cache entries configured by external
333 * applications
334 */
pmksa_cache_flush(struct rsn_pmksa_cache * pmksa,void * network_ctx,const u8 * pmk,size_t pmk_len,bool external_only)335 void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
336 const u8 *pmk, size_t pmk_len, bool external_only)
337 {
338 struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp;
339 int removed = 0;
340
341 entry = pmksa->pmksa;
342 while (entry) {
343 if ((entry->network_ctx == network_ctx ||
344 network_ctx == NULL) &&
345 (pmk == NULL ||
346 (pmk_len == entry->pmk_len &&
347 os_memcmp(pmk, entry->pmk, pmk_len) == 0)) &&
348 (!external_only || entry->external)) {
349 wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry "
350 "for " MACSTR, MAC2STR(entry->aa));
351 if (prev)
352 prev->next = entry->next;
353 else
354 pmksa->pmksa = entry->next;
355 tmp = entry;
356 entry = entry->next;
357 pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE);
358 removed++;
359 } else {
360 prev = entry;
361 entry = entry->next;
362 }
363 }
364 if (removed)
365 pmksa_cache_set_expiration(pmksa);
366 }
367
368
369 /**
370 * pmksa_cache_deinit - Free all entries in PMKSA cache
371 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
372 */
pmksa_cache_deinit(struct rsn_pmksa_cache * pmksa)373 void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
374 {
375 struct rsn_pmksa_cache_entry *entry, *prev;
376
377 if (pmksa == NULL)
378 return;
379
380 entry = pmksa->pmksa;
381 pmksa->pmksa = NULL;
382 while (entry) {
383 prev = entry;
384 entry = entry->next;
385 os_free(prev);
386 }
387 pmksa_cache_set_expiration(pmksa);
388 os_free(pmksa);
389 }
390
391
392 /**
393 * pmksa_cache_get - Fetch a PMKSA cache entry
394 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
395 * @aa: Authenticator address or %NULL to match any
396 * @pmkid: PMKID or %NULL to match any
397 * @network_ctx: Network context or %NULL to match any
398 * @akmp: Specific AKMP to search for or 0 for any
399 * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
400 */
pmksa_cache_get(struct rsn_pmksa_cache * pmksa,const u8 * aa,const u8 * pmkid,const void * network_ctx,int akmp)401 struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
402 const u8 *aa, const u8 *pmkid,
403 const void *network_ctx,
404 int akmp)
405 {
406 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
407 while (entry) {
408 if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
409 (pmkid == NULL ||
410 os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) &&
411 (!akmp || akmp == entry->akmp) &&
412 (network_ctx == NULL || network_ctx == entry->network_ctx))
413 return entry;
414 entry = entry->next;
415 }
416 return NULL;
417 }
418
419
420 static struct rsn_pmksa_cache_entry *
pmksa_cache_clone_entry(struct rsn_pmksa_cache * pmksa,const struct rsn_pmksa_cache_entry * old_entry,const u8 * aa)421 pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
422 const struct rsn_pmksa_cache_entry *old_entry,
423 const u8 *aa)
424 {
425 struct rsn_pmksa_cache_entry *new_entry;
426 os_time_t old_expiration = old_entry->expiration;
427 os_time_t old_reauth_time = old_entry->reauth_time;
428 const u8 *pmkid = NULL;
429
430 if (wpa_key_mgmt_sae(old_entry->akmp) ||
431 wpa_key_mgmt_fils(old_entry->akmp))
432 pmkid = old_entry->pmkid;
433 new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
434 pmkid, NULL, 0,
435 aa, pmksa->sm->own_addr,
436 old_entry->network_ctx, old_entry->akmp,
437 old_entry->fils_cache_id_set ?
438 old_entry->fils_cache_id : NULL);
439 if (new_entry == NULL)
440 return NULL;
441
442 /* TODO: reorder entries based on expiration time? */
443 new_entry->expiration = old_expiration;
444 new_entry->reauth_time = old_reauth_time;
445 new_entry->opportunistic = 1;
446
447 return new_entry;
448 }
449
450
451 /**
452 * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry
453 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
454 * @network_ctx: Network configuration context
455 * @aa: Authenticator address for the new AP
456 * @akmp: Specific AKMP to search for or 0 for any
457 * Returns: Pointer to a new PMKSA cache entry or %NULL if not available
458 *
459 * Try to create a new PMKSA cache entry opportunistically by guessing that the
460 * new AP is sharing the same PMK as another AP that has the same SSID and has
461 * already an entry in PMKSA cache.
462 */
463 struct rsn_pmksa_cache_entry *
pmksa_cache_get_opportunistic(struct rsn_pmksa_cache * pmksa,void * network_ctx,const u8 * aa,int akmp)464 pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
465 const u8 *aa, int akmp)
466 {
467 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
468
469 wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa));
470 if (network_ctx == NULL)
471 return NULL;
472 while (entry) {
473 if (entry->network_ctx == network_ctx &&
474 (!akmp || entry->akmp == akmp)) {
475 struct os_reltime now;
476
477 if (wpa_key_mgmt_sae(entry->akmp) &&
478 os_get_reltime(&now) == 0 &&
479 entry->reauth_time < now.sec) {
480 wpa_printf(MSG_DEBUG,
481 "RSN: Do not clone PMKSA cache entry for "
482 MACSTR
483 " since its reauth threshold has passed",
484 MAC2STR(entry->aa));
485 entry = entry->next;
486 continue;
487 }
488
489 entry = pmksa_cache_clone_entry(pmksa, entry, aa);
490 if (entry) {
491 wpa_printf(MSG_DEBUG, "RSN: added "
492 "opportunistic PMKSA cache entry "
493 "for " MACSTR, MAC2STR(aa));
494 }
495 return entry;
496 }
497 entry = entry->next;
498 }
499 return NULL;
500 }
501
502 #ifdef CONFIG_FILS
503 static struct rsn_pmksa_cache_entry *
pmksa_cache_get_fils_cache_id(struct rsn_pmksa_cache * pmksa,const void * network_ctx,const u8 * cache_id)504 pmksa_cache_get_fils_cache_id(struct rsn_pmksa_cache *pmksa,
505 const void *network_ctx, const u8 *cache_id)
506 {
507 struct rsn_pmksa_cache_entry *entry;
508
509 for (entry = pmksa->pmksa; entry; entry = entry->next) {
510 if (network_ctx == entry->network_ctx &&
511 entry->fils_cache_id_set &&
512 os_memcmp(cache_id, entry->fils_cache_id,
513 FILS_CACHE_ID_LEN) == 0)
514 return entry;
515 }
516
517 return NULL;
518 }
519 #endif /* CONFIG_FILS */
520
521
522 /**
523 * pmksa_cache_get_current - Get the current used PMKSA entry
524 * @sm: Pointer to WPA state machine data from wpa_sm_init()
525 * Returns: Pointer to the current PMKSA cache entry or %NULL if not available
526 */
pmksa_cache_get_current(struct wpa_sm * sm)527 struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm)
528 {
529 if (sm == NULL)
530 return NULL;
531 return sm->cur_pmksa;
532 }
533
534
535 /**
536 * pmksa_cache_clear_current - Clear the current PMKSA entry selection
537 * @sm: Pointer to WPA state machine data from wpa_sm_init()
538 */
pmksa_cache_clear_current(struct wpa_sm * sm)539 void pmksa_cache_clear_current(struct wpa_sm *sm)
540 {
541 if (sm == NULL)
542 return;
543 if (sm->cur_pmksa)
544 wpa_printf(MSG_DEBUG,
545 "RSN: Clear current PMKSA entry selection");
546 sm->cur_pmksa = NULL;
547 }
548
549
550 /**
551 * pmksa_cache_set_current - Set the current PMKSA entry selection
552 * @sm: Pointer to WPA state machine data from wpa_sm_init()
553 * @pmkid: PMKID for selecting PMKSA or %NULL if not used
554 * @bssid: BSSID for PMKSA or %NULL if not used
555 * @network_ctx: Network configuration context
556 * @try_opportunistic: Whether to allow opportunistic PMKSA caching
557 * @fils_cache_id: Pointer to FILS Cache Identifier or %NULL if not used
558 * Returns: 0 if PMKSA was found or -1 if no matching entry was found
559 */
pmksa_cache_set_current(struct wpa_sm * sm,const u8 * pmkid,const u8 * bssid,void * network_ctx,int try_opportunistic,const u8 * fils_cache_id,int akmp)560 int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
561 const u8 *bssid, void *network_ctx,
562 int try_opportunistic, const u8 *fils_cache_id,
563 int akmp)
564 {
565 struct rsn_pmksa_cache *pmksa = sm->pmksa;
566 wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p "
567 "try_opportunistic=%d akmp=0x%x",
568 network_ctx, try_opportunistic, akmp);
569 if (pmkid)
570 wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID",
571 pmkid, PMKID_LEN);
572 if (bssid)
573 wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR,
574 MAC2STR(bssid));
575 #ifdef CONFIG_FILS
576 if (fils_cache_id)
577 wpa_printf(MSG_DEBUG,
578 "RSN: Search for FILS Cache Identifier %02x%02x",
579 fils_cache_id[0], fils_cache_id[1]);
580 #endif /* CONFIG_FILS */
581
582 sm->cur_pmksa = NULL;
583 if (pmkid)
584 sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid,
585 network_ctx, akmp);
586 if (sm->cur_pmksa == NULL && bssid)
587 sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL,
588 network_ctx, akmp);
589 #ifndef LOS_WPA_PATCH
590 if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
591 sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
592 network_ctx,
593 bssid, akmp);
594 if (sm->cur_pmksa == NULL && fils_cache_id)
595 sm->cur_pmksa = pmksa_cache_get_fils_cache_id(pmksa,
596 network_ctx,
597 fils_cache_id);
598 #endif /* LOS_WPA_PATCH */
599 if (sm->cur_pmksa) {
600 struct os_reltime now;
601
602 if (wpa_key_mgmt_sae(sm->cur_pmksa->akmp) &&
603 os_get_reltime(&now) == 0 &&
604 sm->cur_pmksa->reauth_time < now.sec) {
605 wpa_printf(MSG_DEBUG,
606 "RSN: Do not allow PMKSA cache entry for "
607 MACSTR
608 " to be used for SAE since its reauth threshold has passed",
609 MAC2STR(sm->cur_pmksa->aa));
610 sm->cur_pmksa = NULL;
611 return -1;
612 }
613
614 wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID",
615 sm->cur_pmksa->pmkid, PMKID_LEN);
616 return 0;
617 }
618 wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found");
619 return -1;
620 }
621
622
623 /**
624 * pmksa_cache_list - Dump text list of entries in PMKSA cache
625 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
626 * @buf: Buffer for the list
627 * @len: Length of the buffer
628 * Returns: number of bytes written to buffer
629 *
630 * This function is used to generate a text format representation of the
631 * current PMKSA cache contents for the ctrl_iface PMKSA command.
632 */
pmksa_cache_list(struct rsn_pmksa_cache * pmksa,char * buf,size_t len)633 int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
634 {
635 int i, ret;
636 char *pos = buf;
637 struct rsn_pmksa_cache_entry *entry;
638 struct os_reltime now;
639 int cache_id_used = 0;
640
641 for (entry = pmksa->pmksa; entry; entry = entry->next) {
642 if (entry->fils_cache_id_set) {
643 cache_id_used = 1;
644 break;
645 }
646 }
647
648 os_get_reltime(&now);
649 ret = os_snprintf(pos, buf + len - pos,
650 "Index / AA / PMKID / expiration (in seconds) / "
651 "opportunistic%s\n",
652 cache_id_used ? " / FILS Cache Identifier" : "");
653 if (os_snprintf_error(buf + len - pos, ret))
654 return pos - buf;
655 pos += ret;
656 i = 0;
657 entry = pmksa->pmksa;
658 while (entry) {
659 i++;
660 ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
661 i, MAC2STR(entry->aa));
662 if (os_snprintf_error(buf + len - pos, ret))
663 return pos - buf;
664 pos += ret;
665 pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
666 PMKID_LEN);
667 ret = os_snprintf(pos, buf + len - pos, " %d %d",
668 (int) (entry->expiration - now.sec),
669 entry->opportunistic);
670 if (os_snprintf_error(buf + len - pos, ret))
671 return pos - buf;
672 pos += ret;
673 if (entry->fils_cache_id_set) {
674 ret = os_snprintf(pos, buf + len - pos, " %02x%02x",
675 entry->fils_cache_id[0],
676 entry->fils_cache_id[1]);
677 if (os_snprintf_error(buf + len - pos, ret))
678 return pos - buf;
679 pos += ret;
680 }
681 ret = os_snprintf(pos, buf + len - pos, "\n");
682 if (os_snprintf_error(buf + len - pos, ret))
683 return pos - buf;
684 pos += ret;
685 entry = entry->next;
686 }
687 return pos - buf;
688 }
689
690
pmksa_cache_head(struct rsn_pmksa_cache * pmksa)691 struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa)
692 {
693 return pmksa->pmksa;
694 }
695
696
697 /**
698 * pmksa_cache_init - Initialize PMKSA cache
699 * @free_cb: Callback function to be called when a PMKSA cache entry is freed
700 * @ctx: Context pointer for free_cb function
701 * @sm: Pointer to WPA state machine data from wpa_sm_init()
702 * Returns: Pointer to PMKSA cache data or %NULL on failure
703 */
704 struct rsn_pmksa_cache *
pmksa_cache_init(void (* free_cb)(struct rsn_pmksa_cache_entry * entry,void * ctx,enum pmksa_free_reason reason),bool (* is_current_cb)(struct rsn_pmksa_cache_entry * entry,void * ctx),void * ctx,struct wpa_sm * sm)705 pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
706 void *ctx, enum pmksa_free_reason reason),
707 bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
708 void *ctx),
709 void *ctx, struct wpa_sm *sm)
710 {
711 struct rsn_pmksa_cache *pmksa;
712
713 pmksa = os_zalloc(sizeof(*pmksa));
714 if (pmksa) {
715 pmksa->free_cb = free_cb;
716 pmksa->is_current_cb = is_current_cb;
717 pmksa->ctx = ctx;
718 pmksa->sm = sm;
719 }
720
721 return pmksa;
722 }
723
724
pmksa_cache_reconfig(struct rsn_pmksa_cache * pmksa)725 void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa)
726 {
727 struct rsn_pmksa_cache_entry *entry;
728 struct os_reltime now;
729
730 if (!pmksa || !pmksa->pmksa)
731 return;
732
733 os_get_reltime(&now);
734 for (entry = pmksa->pmksa; entry; entry = entry->next) {
735 u32 life_time;
736 u8 reauth_threshold;
737
738 if (entry->expiration - now.sec < 1 ||
739 entry->reauth_time - now.sec < 1)
740 continue;
741
742 life_time = entry->expiration - now.sec;
743 reauth_threshold = (entry->reauth_time - now.sec) * 100 /
744 life_time;
745 if (!reauth_threshold)
746 continue;
747
748 wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
749 entry->pmkid,
750 entry->fils_cache_id_set ?
751 entry->fils_cache_id : NULL,
752 entry->pmk, entry->pmk_len, life_time,
753 reauth_threshold, entry->akmp);
754 }
755 }
756
757 #endif /* IEEE8021X_EAPOL */
758