]> git.ipfire.org Git - thirdparty/hostap.git/blob - src/ap/pmksa_cache_auth.c
Fix WPA/RSN IE update on reconfig with set_generic_elem()
[thirdparty/hostap.git] / src / ap / pmksa_cache_auth.c
1 /*
2 * hostapd - PMKSA cache for IEEE 802.11i RSN
3 * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
11 *
12 * See README and COPYING for more details.
13 */
14
15 #include "utils/includes.h"
16
17 #include "utils/common.h"
18 #include "utils/eloop.h"
19 #include "eapol_auth/eapol_auth_sm.h"
20 #include "eapol_auth/eapol_auth_sm_i.h"
21 #include "sta_info.h"
22 #include "ap_config.h"
23 #include "pmksa_cache_auth.h"
24
25
26 static const int pmksa_cache_max_entries = 1024;
27 static const int dot11RSNAConfigPMKLifetime = 43200;
28
29 struct rsn_pmksa_cache {
30 #define PMKID_HASH_SIZE 128
31 #define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f)
32 struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE];
33 struct rsn_pmksa_cache_entry *pmksa;
34 int pmksa_count;
35
36 void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
37 void *ctx;
38 };
39
40
41 static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
42
43
44 static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
45 {
46 if (entry == NULL)
47 return;
48 os_free(entry->identity);
49 #ifndef CONFIG_NO_RADIUS
50 radius_free_class(&entry->radius_class);
51 #endif /* CONFIG_NO_RADIUS */
52 os_free(entry);
53 }
54
55
56 static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
57 struct rsn_pmksa_cache_entry *entry)
58 {
59 struct rsn_pmksa_cache_entry *pos, *prev;
60
61 pmksa->pmksa_count--;
62 pmksa->free_cb(entry, pmksa->ctx);
63 pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
64 prev = NULL;
65 while (pos) {
66 if (pos == entry) {
67 if (prev != NULL) {
68 prev->hnext = pos->hnext;
69 } else {
70 pmksa->pmkid[PMKID_HASH(entry->pmkid)] =
71 pos->hnext;
72 }
73 break;
74 }
75 prev = pos;
76 pos = pos->hnext;
77 }
78
79 pos = pmksa->pmksa;
80 prev = NULL;
81 while (pos) {
82 if (pos == entry) {
83 if (prev != NULL)
84 prev->next = pos->next;
85 else
86 pmksa->pmksa = pos->next;
87 break;
88 }
89 prev = pos;
90 pos = pos->next;
91 }
92 _pmksa_cache_free_entry(entry);
93 }
94
95
96 static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
97 {
98 struct rsn_pmksa_cache *pmksa = eloop_ctx;
99 struct os_time now;
100
101 os_get_time(&now);
102 while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
103 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
104 pmksa->pmksa = entry->next;
105 wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
106 MACSTR, MAC2STR(entry->spa));
107 pmksa_cache_free_entry(pmksa, entry);
108 }
109
110 pmksa_cache_set_expiration(pmksa);
111 }
112
113
114 static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
115 {
116 int sec;
117 struct os_time now;
118
119 eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
120 if (pmksa->pmksa == NULL)
121 return;
122 os_get_time(&now);
123 sec = pmksa->pmksa->expiration - now.sec;
124 if (sec < 0)
125 sec = 0;
126 eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
127 }
128
129
130 static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
131 struct eapol_state_machine *eapol)
132 {
133 if (eapol == NULL)
134 return;
135
136 if (eapol->identity) {
137 entry->identity = os_malloc(eapol->identity_len);
138 if (entry->identity) {
139 entry->identity_len = eapol->identity_len;
140 os_memcpy(entry->identity, eapol->identity,
141 eapol->identity_len);
142 }
143 }
144
145 #ifndef CONFIG_NO_RADIUS
146 radius_copy_class(&entry->radius_class, &eapol->radius_class);
147 #endif /* CONFIG_NO_RADIUS */
148
149 entry->eap_type_authsrv = eapol->eap_type_authsrv;
150 entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id;
151 }
152
153
154 void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
155 struct eapol_state_machine *eapol)
156 {
157 if (entry == NULL || eapol == NULL)
158 return;
159
160 if (entry->identity) {
161 os_free(eapol->identity);
162 eapol->identity = os_malloc(entry->identity_len);
163 if (eapol->identity) {
164 eapol->identity_len = entry->identity_len;
165 os_memcpy(eapol->identity, entry->identity,
166 entry->identity_len);
167 }
168 wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA",
169 eapol->identity, eapol->identity_len);
170 }
171
172 #ifndef CONFIG_NO_RADIUS
173 radius_free_class(&eapol->radius_class);
174 radius_copy_class(&eapol->radius_class, &entry->radius_class);
175 #endif /* CONFIG_NO_RADIUS */
176 if (eapol->radius_class.attr) {
177 wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from "
178 "PMKSA", (unsigned long) eapol->radius_class.count);
179 }
180
181 eapol->eap_type_authsrv = entry->eap_type_authsrv;
182 ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id;
183 }
184
185
186 static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
187 struct rsn_pmksa_cache_entry *entry)
188 {
189 struct rsn_pmksa_cache_entry *pos, *prev;
190
191 /* Add the new entry; order by expiration time */
192 pos = pmksa->pmksa;
193 prev = NULL;
194 while (pos) {
195 if (pos->expiration > entry->expiration)
196 break;
197 prev = pos;
198 pos = pos->next;
199 }
200 if (prev == NULL) {
201 entry->next = pmksa->pmksa;
202 pmksa->pmksa = entry;
203 } else {
204 entry->next = prev->next;
205 prev->next = entry;
206 }
207 entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
208 pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry;
209
210 pmksa->pmksa_count++;
211 wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
212 MAC2STR(entry->spa));
213 wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
214 }
215
216
217 /**
218 * pmksa_cache_auth_add - Add a PMKSA cache entry
219 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
220 * @pmk: The new pairwise master key
221 * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
222 * @aa: Authenticator address
223 * @spa: Supplicant address
224 * @session_timeout: Session timeout
225 * @eapol: Pointer to EAPOL state machine data
226 * @akmp: WPA_KEY_MGMT_* used in key derivation
227 * Returns: Pointer to the added PMKSA cache entry or %NULL on error
228 *
229 * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
230 * cache. If an old entry is already in the cache for the same Supplicant,
231 * this entry will be replaced with the new entry. PMKID will be calculated
232 * based on the PMK.
233 */
234 struct rsn_pmksa_cache_entry *
235 pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
236 const u8 *pmk, size_t pmk_len,
237 const u8 *aa, const u8 *spa, int session_timeout,
238 struct eapol_state_machine *eapol, int akmp)
239 {
240 struct rsn_pmksa_cache_entry *entry, *pos;
241 struct os_time now;
242
243 if (pmk_len > PMK_LEN)
244 return NULL;
245
246 entry = os_zalloc(sizeof(*entry));
247 if (entry == NULL)
248 return NULL;
249 os_memcpy(entry->pmk, pmk, pmk_len);
250 entry->pmk_len = pmk_len;
251 rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
252 wpa_key_mgmt_sha256(akmp));
253 os_get_time(&now);
254 entry->expiration = now.sec;
255 if (session_timeout > 0)
256 entry->expiration += session_timeout;
257 else
258 entry->expiration += dot11RSNAConfigPMKLifetime;
259 entry->akmp = akmp;
260 os_memcpy(entry->spa, spa, ETH_ALEN);
261 pmksa_cache_from_eapol_data(entry, eapol);
262
263 /* Replace an old entry for the same STA (if found) with the new entry
264 */
265 pos = pmksa_cache_auth_get(pmksa, spa, NULL);
266 if (pos)
267 pmksa_cache_free_entry(pmksa, pos);
268
269 if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
270 /* Remove the oldest entry to make room for the new entry */
271 wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
272 "entry (for " MACSTR ") to make room for new one",
273 MAC2STR(pmksa->pmksa->spa));
274 pmksa_cache_free_entry(pmksa, pmksa->pmksa);
275 }
276
277 pmksa_cache_link_entry(pmksa, entry);
278
279 return entry;
280 }
281
282
283 struct rsn_pmksa_cache_entry *
284 pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
285 const struct rsn_pmksa_cache_entry *old_entry,
286 const u8 *aa, const u8 *pmkid)
287 {
288 struct rsn_pmksa_cache_entry *entry;
289
290 entry = os_zalloc(sizeof(*entry));
291 if (entry == NULL)
292 return NULL;
293 os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
294 os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len);
295 entry->pmk_len = old_entry->pmk_len;
296 entry->expiration = old_entry->expiration;
297 entry->akmp = old_entry->akmp;
298 os_memcpy(entry->spa, old_entry->spa, ETH_ALEN);
299 entry->opportunistic = 1;
300 if (old_entry->identity) {
301 entry->identity = os_malloc(old_entry->identity_len);
302 if (entry->identity) {
303 entry->identity_len = old_entry->identity_len;
304 os_memcpy(entry->identity, old_entry->identity,
305 old_entry->identity_len);
306 }
307 }
308 #ifndef CONFIG_NO_RADIUS
309 radius_copy_class(&entry->radius_class, &old_entry->radius_class);
310 #endif /* CONFIG_NO_RADIUS */
311 entry->eap_type_authsrv = old_entry->eap_type_authsrv;
312 entry->vlan_id = old_entry->vlan_id;
313 entry->opportunistic = 1;
314
315 pmksa_cache_link_entry(pmksa, entry);
316
317 return entry;
318 }
319
320
321 /**
322 * pmksa_cache_auth_deinit - Free all entries in PMKSA cache
323 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
324 */
325 void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
326 {
327 struct rsn_pmksa_cache_entry *entry, *prev;
328 int i;
329
330 if (pmksa == NULL)
331 return;
332
333 entry = pmksa->pmksa;
334 while (entry) {
335 prev = entry;
336 entry = entry->next;
337 _pmksa_cache_free_entry(prev);
338 }
339 eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
340 for (i = 0; i < PMKID_HASH_SIZE; i++)
341 pmksa->pmkid[i] = NULL;
342 os_free(pmksa);
343 }
344
345
346 /**
347 * pmksa_cache_auth_get - Fetch a PMKSA cache entry
348 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
349 * @spa: Supplicant address or %NULL to match any
350 * @pmkid: PMKID or %NULL to match any
351 * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
352 */
353 struct rsn_pmksa_cache_entry *
354 pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
355 const u8 *spa, const u8 *pmkid)
356 {
357 struct rsn_pmksa_cache_entry *entry;
358
359 if (pmkid)
360 entry = pmksa->pmkid[PMKID_HASH(pmkid)];
361 else
362 entry = pmksa->pmksa;
363 while (entry) {
364 if ((spa == NULL ||
365 os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
366 (pmkid == NULL ||
367 os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
368 return entry;
369 entry = pmkid ? entry->hnext : entry->next;
370 }
371 return NULL;
372 }
373
374
375 /**
376 * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC
377 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
378 * @aa: Authenticator address
379 * @spa: Supplicant address
380 * @pmkid: PMKID
381 * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
382 *
383 * Use opportunistic key caching (OKC) to find a PMK for a supplicant.
384 */
385 struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
386 struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
387 const u8 *pmkid)
388 {
389 struct rsn_pmksa_cache_entry *entry;
390 u8 new_pmkid[PMKID_LEN];
391
392 entry = pmksa->pmksa;
393 while (entry) {
394 if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
395 continue;
396 rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
397 wpa_key_mgmt_sha256(entry->akmp));
398 if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
399 return entry;
400 entry = entry->next;
401 }
402 return NULL;
403 }
404
405
406 /**
407 * pmksa_cache_auth_init - Initialize PMKSA cache
408 * @free_cb: Callback function to be called when a PMKSA cache entry is freed
409 * @ctx: Context pointer for free_cb function
410 * Returns: Pointer to PMKSA cache data or %NULL on failure
411 */
412 struct rsn_pmksa_cache *
413 pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
414 void *ctx), void *ctx)
415 {
416 struct rsn_pmksa_cache *pmksa;
417
418 pmksa = os_zalloc(sizeof(*pmksa));
419 if (pmksa) {
420 pmksa->free_cb = free_cb;
421 pmksa->ctx = ctx;
422 }
423
424 return pmksa;
425 }