/src/hostap/src/ap/pmksa_cache_auth.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | #define PMKID_HASH_SIZE 128 |
26 | 0 | #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 | 0 | { |
41 | 0 | os_free(entry->vlan_desc); |
42 | 0 | os_free(entry->identity); |
43 | 0 | os_free(entry->dpp_pkhash); |
44 | 0 | wpabuf_free(entry->cui); |
45 | 0 | #ifndef CONFIG_NO_RADIUS |
46 | 0 | radius_free_class(&entry->radius_class); |
47 | 0 | #endif /* CONFIG_NO_RADIUS */ |
48 | 0 | bin_clear_free(entry, sizeof(*entry)); |
49 | 0 | } |
50 | | |
51 | | |
52 | | void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, |
53 | | struct rsn_pmksa_cache_entry *entry) |
54 | 0 | { |
55 | 0 | struct rsn_pmksa_cache_entry *pos, *prev; |
56 | 0 | unsigned int hash; |
57 | |
|
58 | 0 | pmksa->pmksa_count--; |
59 | |
|
60 | 0 | if (pmksa->free_cb) |
61 | 0 | pmksa->free_cb(entry, pmksa->ctx); |
62 | | |
63 | | /* unlink from hash list */ |
64 | 0 | hash = PMKID_HASH(entry->pmkid); |
65 | 0 | pos = pmksa->pmkid[hash]; |
66 | 0 | prev = NULL; |
67 | 0 | while (pos) { |
68 | 0 | if (pos == entry) { |
69 | 0 | if (prev != NULL) |
70 | 0 | prev->hnext = entry->hnext; |
71 | 0 | else |
72 | 0 | pmksa->pmkid[hash] = entry->hnext; |
73 | 0 | break; |
74 | 0 | } |
75 | 0 | prev = pos; |
76 | 0 | pos = pos->hnext; |
77 | 0 | } |
78 | | |
79 | | /* unlink from entry list */ |
80 | 0 | pos = pmksa->pmksa; |
81 | 0 | prev = NULL; |
82 | 0 | while (pos) { |
83 | 0 | if (pos == entry) { |
84 | 0 | if (prev != NULL) |
85 | 0 | prev->next = entry->next; |
86 | 0 | else |
87 | 0 | pmksa->pmksa = entry->next; |
88 | 0 | break; |
89 | 0 | } |
90 | 0 | prev = pos; |
91 | 0 | pos = pos->next; |
92 | 0 | } |
93 | |
|
94 | 0 | _pmksa_cache_free_entry(entry); |
95 | 0 | } |
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 | 0 | { |
104 | 0 | while (pmksa->pmksa) { |
105 | 0 | wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry for " |
106 | 0 | MACSTR, MAC2STR(pmksa->pmksa->spa)); |
107 | 0 | pmksa_cache_free_entry(pmksa, pmksa->pmksa); |
108 | 0 | } |
109 | 0 | } |
110 | | |
111 | | |
112 | | static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) |
113 | 0 | { |
114 | 0 | struct rsn_pmksa_cache *pmksa = eloop_ctx; |
115 | 0 | struct os_reltime now; |
116 | |
|
117 | 0 | os_get_reltime(&now); |
118 | 0 | while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { |
119 | 0 | wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " |
120 | 0 | MACSTR, MAC2STR(pmksa->pmksa->spa)); |
121 | 0 | pmksa_cache_free_entry(pmksa, pmksa->pmksa); |
122 | 0 | } |
123 | |
|
124 | 0 | pmksa_cache_set_expiration(pmksa); |
125 | 0 | } |
126 | | |
127 | | |
128 | | static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) |
129 | 0 | { |
130 | 0 | int sec; |
131 | 0 | struct os_reltime now; |
132 | |
|
133 | 0 | eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); |
134 | 0 | if (pmksa->pmksa == NULL) |
135 | 0 | return; |
136 | 0 | os_get_reltime(&now); |
137 | 0 | sec = pmksa->pmksa->expiration - now.sec; |
138 | 0 | if (sec < 0) |
139 | 0 | sec = 0; |
140 | 0 | eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); |
141 | 0 | } |
142 | | |
143 | | |
144 | | static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, |
145 | | struct eapol_state_machine *eapol) |
146 | 0 | { |
147 | 0 | struct vlan_description *vlan_desc; |
148 | |
|
149 | 0 | if (eapol == NULL) |
150 | 0 | return; |
151 | | |
152 | 0 | if (eapol->identity) { |
153 | 0 | entry->identity = os_malloc(eapol->identity_len); |
154 | 0 | if (entry->identity) { |
155 | 0 | entry->identity_len = eapol->identity_len; |
156 | 0 | os_memcpy(entry->identity, eapol->identity, |
157 | 0 | eapol->identity_len); |
158 | 0 | } |
159 | 0 | } |
160 | |
|
161 | 0 | if (eapol->radius_cui) |
162 | 0 | entry->cui = wpabuf_dup(eapol->radius_cui); |
163 | |
|
164 | 0 | #ifndef CONFIG_NO_RADIUS |
165 | 0 | radius_copy_class(&entry->radius_class, &eapol->radius_class); |
166 | 0 | #endif /* CONFIG_NO_RADIUS */ |
167 | |
|
168 | 0 | entry->eap_type_authsrv = eapol->eap_type_authsrv; |
169 | |
|
170 | 0 | vlan_desc = ((struct sta_info *) eapol->sta)->vlan_desc; |
171 | 0 | if (vlan_desc && vlan_desc->notempty) { |
172 | 0 | entry->vlan_desc = os_zalloc(sizeof(struct vlan_description)); |
173 | 0 | if (entry->vlan_desc) |
174 | 0 | *entry->vlan_desc = *vlan_desc; |
175 | 0 | } else { |
176 | 0 | entry->vlan_desc = NULL; |
177 | 0 | } |
178 | |
|
179 | 0 | entry->acct_multi_session_id = eapol->acct_multi_session_id; |
180 | 0 | } |
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 | 0 | { |
187 | 0 | if (entry == NULL || eapol == NULL) |
188 | 0 | return; |
189 | | |
190 | 0 | if (entry->identity) { |
191 | 0 | os_free(eapol->identity); |
192 | 0 | eapol->identity = os_malloc(entry->identity_len); |
193 | 0 | if (eapol->identity) { |
194 | 0 | eapol->identity_len = entry->identity_len; |
195 | 0 | os_memcpy(eapol->identity, entry->identity, |
196 | 0 | entry->identity_len); |
197 | 0 | } |
198 | 0 | wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA", |
199 | 0 | eapol->identity, eapol->identity_len); |
200 | 0 | } |
201 | |
|
202 | 0 | if (entry->cui) { |
203 | 0 | wpabuf_free(eapol->radius_cui); |
204 | 0 | eapol->radius_cui = wpabuf_dup(entry->cui); |
205 | 0 | } |
206 | |
|
207 | 0 | #ifndef CONFIG_NO_RADIUS |
208 | 0 | radius_free_class(&eapol->radius_class); |
209 | 0 | radius_copy_class(&eapol->radius_class, &entry->radius_class); |
210 | 0 | #endif /* CONFIG_NO_RADIUS */ |
211 | 0 | if (eapol->radius_class.attr) { |
212 | 0 | wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from " |
213 | 0 | "PMKSA", (unsigned long) eapol->radius_class.count); |
214 | 0 | } |
215 | |
|
216 | 0 | eapol->eap_type_authsrv = entry->eap_type_authsrv; |
217 | 0 | #ifndef CONFIG_NO_VLAN |
218 | 0 | ap_sta_set_vlan(hapd, eapol->sta, entry->vlan_desc); |
219 | 0 | #endif /* CONFIG_NO_VLAN */ |
220 | |
|
221 | 0 | eapol->acct_multi_session_id = entry->acct_multi_session_id; |
222 | 0 | } |
223 | | |
224 | | |
225 | | static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, |
226 | | struct rsn_pmksa_cache_entry *entry) |
227 | 0 | { |
228 | 0 | struct rsn_pmksa_cache_entry *pos, *prev; |
229 | 0 | int hash; |
230 | | |
231 | | /* Add the new entry; order by expiration time */ |
232 | 0 | pos = pmksa->pmksa; |
233 | 0 | prev = NULL; |
234 | 0 | while (pos) { |
235 | 0 | if (pos->expiration > entry->expiration) |
236 | 0 | break; |
237 | 0 | prev = pos; |
238 | 0 | pos = pos->next; |
239 | 0 | } |
240 | 0 | if (prev == NULL) { |
241 | 0 | entry->next = pmksa->pmksa; |
242 | 0 | pmksa->pmksa = entry; |
243 | 0 | } else { |
244 | 0 | entry->next = prev->next; |
245 | 0 | prev->next = entry; |
246 | 0 | } |
247 | |
|
248 | 0 | hash = PMKID_HASH(entry->pmkid); |
249 | 0 | entry->hnext = pmksa->pmkid[hash]; |
250 | 0 | pmksa->pmkid[hash] = entry; |
251 | |
|
252 | 0 | pmksa->pmksa_count++; |
253 | 0 | if (prev == NULL) |
254 | 0 | pmksa_cache_set_expiration(pmksa); |
255 | 0 | wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, |
256 | 0 | MAC2STR(entry->spa)); |
257 | 0 | wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN); |
258 | 0 | } |
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 | 0 | { |
288 | 0 | struct rsn_pmksa_cache_entry *entry; |
289 | |
|
290 | 0 | entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len, |
291 | 0 | aa, spa, session_timeout, eapol, |
292 | 0 | akmp); |
293 | |
|
294 | 0 | if (pmksa_cache_auth_add_entry(pmksa, entry) < 0) |
295 | 0 | return NULL; |
296 | | |
297 | 0 | return entry; |
298 | 0 | } |
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 | 0 | { |
323 | 0 | struct rsn_pmksa_cache_entry *entry; |
324 | 0 | struct os_reltime now; |
325 | |
|
326 | 0 | if (pmk_len > PMK_LEN_MAX) |
327 | 0 | return NULL; |
328 | | |
329 | 0 | if (wpa_key_mgmt_suite_b(akmp) && !kck) |
330 | 0 | return NULL; |
331 | | |
332 | 0 | entry = os_zalloc(sizeof(*entry)); |
333 | 0 | if (entry == NULL) |
334 | 0 | return NULL; |
335 | 0 | os_memcpy(entry->pmk, pmk, pmk_len); |
336 | 0 | entry->pmk_len = pmk_len; |
337 | 0 | if (kck && kck_len && kck_len < WPA_KCK_MAX_LEN) { |
338 | 0 | os_memcpy(entry->kck, kck, kck_len); |
339 | 0 | entry->kck_len = kck_len; |
340 | 0 | } |
341 | 0 | if (pmkid) |
342 | 0 | os_memcpy(entry->pmkid, pmkid, PMKID_LEN); |
343 | 0 | else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) |
344 | 0 | rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid); |
345 | 0 | else if (wpa_key_mgmt_suite_b(akmp)) |
346 | 0 | rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); |
347 | 0 | else |
348 | 0 | rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp); |
349 | 0 | os_get_reltime(&now); |
350 | 0 | entry->expiration = now.sec; |
351 | 0 | if (session_timeout > 0) |
352 | 0 | entry->expiration += session_timeout; |
353 | 0 | else |
354 | 0 | entry->expiration += dot11RSNAConfigPMKLifetime; |
355 | 0 | entry->akmp = akmp; |
356 | 0 | os_memcpy(entry->spa, spa, ETH_ALEN); |
357 | 0 | pmksa_cache_from_eapol_data(entry, eapol); |
358 | |
|
359 | 0 | return entry; |
360 | 0 | } |
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 | 0 | { |
375 | 0 | struct rsn_pmksa_cache_entry *pos; |
376 | |
|
377 | 0 | if (entry == NULL) |
378 | 0 | return -1; |
379 | | |
380 | | /* Replace an old entry for the same STA (if found) with the new entry |
381 | | */ |
382 | 0 | pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL); |
383 | 0 | if (pos) |
384 | 0 | pmksa_cache_free_entry(pmksa, pos); |
385 | |
|
386 | 0 | if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { |
387 | | /* Remove the oldest entry to make room for the new entry */ |
388 | 0 | wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache " |
389 | 0 | "entry (for " MACSTR ") to make room for new one", |
390 | 0 | MAC2STR(pmksa->pmksa->spa)); |
391 | 0 | pmksa_cache_free_entry(pmksa, pmksa->pmksa); |
392 | 0 | } |
393 | |
|
394 | 0 | pmksa_cache_link_entry(pmksa, entry); |
395 | |
|
396 | 0 | return 0; |
397 | 0 | } |
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 | 0 | { |
405 | 0 | struct rsn_pmksa_cache_entry *entry; |
406 | |
|
407 | 0 | entry = os_zalloc(sizeof(*entry)); |
408 | 0 | if (entry == NULL) |
409 | 0 | return NULL; |
410 | 0 | os_memcpy(entry->pmkid, pmkid, PMKID_LEN); |
411 | 0 | os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len); |
412 | 0 | entry->pmk_len = old_entry->pmk_len; |
413 | 0 | entry->expiration = old_entry->expiration; |
414 | 0 | entry->akmp = old_entry->akmp; |
415 | 0 | os_memcpy(entry->spa, old_entry->spa, ETH_ALEN); |
416 | 0 | entry->opportunistic = 1; |
417 | 0 | if (old_entry->identity) { |
418 | 0 | entry->identity = os_malloc(old_entry->identity_len); |
419 | 0 | if (entry->identity) { |
420 | 0 | entry->identity_len = old_entry->identity_len; |
421 | 0 | os_memcpy(entry->identity, old_entry->identity, |
422 | 0 | old_entry->identity_len); |
423 | 0 | } |
424 | 0 | } |
425 | 0 | if (old_entry->cui) |
426 | 0 | entry->cui = wpabuf_dup(old_entry->cui); |
427 | 0 | #ifndef CONFIG_NO_RADIUS |
428 | 0 | radius_copy_class(&entry->radius_class, &old_entry->radius_class); |
429 | 0 | #endif /* CONFIG_NO_RADIUS */ |
430 | 0 | entry->eap_type_authsrv = old_entry->eap_type_authsrv; |
431 | 0 | if (old_entry->vlan_desc) { |
432 | 0 | entry->vlan_desc = os_zalloc(sizeof(struct vlan_description)); |
433 | 0 | if (entry->vlan_desc) |
434 | 0 | *entry->vlan_desc = *old_entry->vlan_desc; |
435 | 0 | } else { |
436 | 0 | entry->vlan_desc = NULL; |
437 | 0 | } |
438 | 0 | entry->opportunistic = 1; |
439 | |
|
440 | 0 | pmksa_cache_link_entry(pmksa, entry); |
441 | |
|
442 | 0 | return entry; |
443 | 0 | } |
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 | 0 | { |
452 | 0 | struct rsn_pmksa_cache_entry *entry, *prev; |
453 | 0 | int i; |
454 | |
|
455 | 0 | if (pmksa == NULL) |
456 | 0 | return; |
457 | | |
458 | 0 | entry = pmksa->pmksa; |
459 | 0 | while (entry) { |
460 | 0 | prev = entry; |
461 | 0 | entry = entry->next; |
462 | 0 | _pmksa_cache_free_entry(prev); |
463 | 0 | } |
464 | 0 | eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); |
465 | 0 | pmksa->pmksa_count = 0; |
466 | 0 | pmksa->pmksa = NULL; |
467 | 0 | for (i = 0; i < PMKID_HASH_SIZE; i++) |
468 | 0 | pmksa->pmkid[i] = NULL; |
469 | 0 | os_free(pmksa); |
470 | 0 | } |
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 | 0 | { |
484 | 0 | struct rsn_pmksa_cache_entry *entry; |
485 | |
|
486 | 0 | if (pmkid) { |
487 | 0 | for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry; |
488 | 0 | entry = entry->hnext) { |
489 | 0 | if ((spa == NULL || |
490 | 0 | ether_addr_equal(entry->spa, spa)) && |
491 | 0 | os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) |
492 | 0 | return entry; |
493 | 0 | } |
494 | 0 | } else { |
495 | 0 | for (entry = pmksa->pmksa; entry; entry = entry->next) { |
496 | 0 | if (spa == NULL || |
497 | 0 | ether_addr_equal(entry->spa, spa)) |
498 | 0 | return entry; |
499 | 0 | } |
500 | 0 | } |
501 | | |
502 | 0 | return NULL; |
503 | 0 | } |
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 | 0 | { |
520 | 0 | struct rsn_pmksa_cache_entry *entry; |
521 | 0 | u8 new_pmkid[PMKID_LEN]; |
522 | |
|
523 | 0 | for (entry = pmksa->pmksa; entry; entry = entry->next) { |
524 | 0 | if (!ether_addr_equal(entry->spa, spa)) |
525 | 0 | continue; |
526 | 0 | if (wpa_key_mgmt_sae(entry->akmp) || |
527 | 0 | wpa_key_mgmt_fils(entry->akmp)) { |
528 | 0 | if (os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) |
529 | 0 | return entry; |
530 | 0 | continue; |
531 | 0 | } |
532 | 0 | if (entry->akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 && |
533 | 0 | entry->kck_len > 0) |
534 | 0 | rsn_pmkid_suite_b_192(entry->kck, entry->kck_len, |
535 | 0 | aa, spa, new_pmkid); |
536 | 0 | else if (wpa_key_mgmt_suite_b(entry->akmp) && |
537 | 0 | entry->kck_len > 0) |
538 | 0 | rsn_pmkid_suite_b(entry->kck, entry->kck_len, aa, spa, |
539 | 0 | new_pmkid); |
540 | 0 | else |
541 | 0 | rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, |
542 | 0 | new_pmkid, entry->akmp); |
543 | 0 | if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0) |
544 | 0 | return entry; |
545 | 0 | } |
546 | 0 | return NULL; |
547 | 0 | } |
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 | 0 | { |
560 | 0 | struct rsn_pmksa_cache *pmksa; |
561 | |
|
562 | 0 | pmksa = os_zalloc(sizeof(*pmksa)); |
563 | 0 | if (pmksa) { |
564 | 0 | pmksa->free_cb = free_cb; |
565 | 0 | pmksa->ctx = ctx; |
566 | 0 | } |
567 | |
|
568 | 0 | return pmksa; |
569 | 0 | } |
570 | | |
571 | | |
572 | | static int das_attr_match(struct rsn_pmksa_cache_entry *entry, |
573 | | struct radius_das_attrs *attr) |
574 | 0 | { |
575 | 0 | int match = 0; |
576 | |
|
577 | 0 | if (attr->sta_addr) { |
578 | 0 | if (!ether_addr_equal(attr->sta_addr, entry->spa)) |
579 | 0 | return 0; |
580 | 0 | match++; |
581 | 0 | } |
582 | | |
583 | 0 | if (attr->acct_multi_session_id) { |
584 | 0 | char buf[20]; |
585 | |
|
586 | 0 | if (attr->acct_multi_session_id_len != 16) |
587 | 0 | return 0; |
588 | 0 | os_snprintf(buf, sizeof(buf), "%016llX", |
589 | 0 | (unsigned long long) entry->acct_multi_session_id); |
590 | 0 | if (os_memcmp(attr->acct_multi_session_id, buf, 16) != 0) |
591 | 0 | return 0; |
592 | 0 | match++; |
593 | 0 | } |
594 | | |
595 | 0 | if (attr->cui) { |
596 | 0 | if (!entry->cui || |
597 | 0 | attr->cui_len != wpabuf_len(entry->cui) || |
598 | 0 | os_memcmp(attr->cui, wpabuf_head(entry->cui), |
599 | 0 | attr->cui_len) != 0) |
600 | 0 | return 0; |
601 | 0 | match++; |
602 | 0 | } |
603 | | |
604 | 0 | if (attr->user_name) { |
605 | 0 | if (!entry->identity || |
606 | 0 | attr->user_name_len != entry->identity_len || |
607 | 0 | os_memcmp(attr->user_name, entry->identity, |
608 | 0 | attr->user_name_len) != 0) |
609 | 0 | return 0; |
610 | 0 | match++; |
611 | 0 | } |
612 | | |
613 | 0 | return match; |
614 | 0 | } |
615 | | |
616 | | |
617 | | int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, |
618 | | struct radius_das_attrs *attr) |
619 | 0 | { |
620 | 0 | int found = 0; |
621 | 0 | struct rsn_pmksa_cache_entry *entry, *prev; |
622 | |
|
623 | 0 | if (attr->acct_session_id) |
624 | 0 | return -1; |
625 | | |
626 | 0 | entry = pmksa->pmksa; |
627 | 0 | while (entry) { |
628 | 0 | if (das_attr_match(entry, attr)) { |
629 | 0 | found++; |
630 | 0 | prev = entry; |
631 | 0 | entry = entry->next; |
632 | 0 | pmksa_cache_free_entry(pmksa, prev); |
633 | 0 | continue; |
634 | 0 | } |
635 | 0 | entry = entry->next; |
636 | 0 | } |
637 | |
|
638 | 0 | return found ? 0 : -1; |
639 | 0 | } |
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 | | * @index: Externally stored index counter |
648 | | * Returns: Number of bytes written to buffer |
649 | | * |
650 | | * This function is used to generate a text format representation of the |
651 | | * current PMKSA cache contents for the ctrl_iface PMKSA command. |
652 | | */ |
653 | | int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len, |
654 | | int *index) |
655 | 0 | { |
656 | 0 | int ret; |
657 | 0 | char *pos = buf; |
658 | 0 | struct rsn_pmksa_cache_entry *entry; |
659 | 0 | struct os_reltime now; |
660 | |
|
661 | 0 | os_get_reltime(&now); |
662 | 0 | entry = pmksa->pmksa; |
663 | 0 | while (entry) { |
664 | 0 | ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", |
665 | 0 | *index, MAC2STR(entry->spa)); |
666 | 0 | if (os_snprintf_error(buf + len - pos, ret)) |
667 | 0 | return pos - buf; |
668 | 0 | pos += ret; |
669 | 0 | pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, |
670 | 0 | PMKID_LEN); |
671 | 0 | ret = os_snprintf(pos, buf + len - pos, " %d %d\n", |
672 | 0 | (int) (entry->expiration - now.sec), |
673 | 0 | entry->opportunistic); |
674 | 0 | if (os_snprintf_error(buf + len - pos, ret)) |
675 | 0 | return pos - buf; |
676 | 0 | pos += ret; |
677 | 0 | entry = entry->next; |
678 | 0 | (*index)++; |
679 | 0 | } |
680 | 0 | return pos - buf; |
681 | 0 | } |
682 | | |
683 | | |
684 | | #ifdef CONFIG_PMKSA_CACHE_EXTERNAL |
685 | | #ifdef CONFIG_MESH |
686 | | |
687 | | /** |
688 | | * pmksa_cache_auth_list_mesh - Dump text list of entries in PMKSA cache |
689 | | * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() |
690 | | * @addr: MAC address of the peer (NULL means any) |
691 | | * @buf: Buffer for the list |
692 | | * @len: Length of the buffer |
693 | | * Returns: Number of bytes written to buffer |
694 | | * |
695 | | * This function is used to generate a text format representation of the |
696 | | * current PMKSA cache contents for the ctrl_iface PMKSA_GET command to store |
697 | | * in external storage. |
698 | | */ |
699 | | int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr, |
700 | | char *buf, size_t len) |
701 | | { |
702 | | int ret; |
703 | | char *pos, *end; |
704 | | struct rsn_pmksa_cache_entry *entry; |
705 | | struct os_reltime now; |
706 | | |
707 | | pos = buf; |
708 | | end = buf + len; |
709 | | os_get_reltime(&now); |
710 | | |
711 | | |
712 | | /* |
713 | | * Entry format: |
714 | | * <BSSID> <PMKID> <PMK> <expiration in seconds> |
715 | | */ |
716 | | for (entry = pmksa->pmksa; entry; entry = entry->next) { |
717 | | if (addr && !ether_addr_equal(entry->spa, addr)) |
718 | | continue; |
719 | | |
720 | | ret = os_snprintf(pos, end - pos, MACSTR " ", |
721 | | MAC2STR(entry->spa)); |
722 | | if (os_snprintf_error(end - pos, ret)) |
723 | | return 0; |
724 | | pos += ret; |
725 | | |
726 | | pos += wpa_snprintf_hex(pos, end - pos, entry->pmkid, |
727 | | PMKID_LEN); |
728 | | |
729 | | ret = os_snprintf(pos, end - pos, " "); |
730 | | if (os_snprintf_error(end - pos, ret)) |
731 | | return 0; |
732 | | pos += ret; |
733 | | |
734 | | pos += wpa_snprintf_hex(pos, end - pos, entry->pmk, |
735 | | entry->pmk_len); |
736 | | |
737 | | ret = os_snprintf(pos, end - pos, " %d\n", |
738 | | (int) (entry->expiration - now.sec)); |
739 | | if (os_snprintf_error(end - pos, ret)) |
740 | | return 0; |
741 | | pos += ret; |
742 | | } |
743 | | |
744 | | return pos - buf; |
745 | | } |
746 | | |
747 | | #endif /* CONFIG_MESH */ |
748 | | #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ |