]> git.ipfire.org Git - thirdparty/hostap.git/blame - src/rsn_supp/pmksa_cache.c
Added support for using SHA256-based stronger key derivation for WPA2
[thirdparty/hostap.git] / src / rsn_supp / pmksa_cache.c
CommitLineData
6fc6879b
JM
1/*
2 * WPA Supplicant - RSN PMKSA cache
56586197 3 * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
6fc6879b
JM
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 "includes.h"
16
17#include "common.h"
18#include "wpa.h"
19#include "eloop.h"
20#include "sha1.h"
56586197 21#include "sha256.h"
6fc6879b
JM
22#include "wpa_i.h"
23#include "eapol_supp/eapol_supp_sm.h"
24#include "pmksa_cache.h"
25
26#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
27
28static const int pmksa_cache_max_entries = 32;
29
30struct rsn_pmksa_cache {
31 struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */
32 int pmksa_count; /* number of entries in PMKSA cache */
33 struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
34
35 void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
36 int replace);
37 void *ctx;
38};
39
40
41/**
42 * rsn_pmkid - Calculate PMK identifier
43 * @pmk: Pairwise master key
44 * @pmk_len: Length of pmk in bytes
45 * @aa: Authenticator address
46 * @spa: Supplicant address
56586197 47 * @use_sha256: Whether to use SHA256-based KDF
6fc6879b
JM
48 *
49 * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
50 * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA)
51 */
56586197
JM
52static void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa,
53 const u8 *spa, u8 *pmkid, int use_sha256)
6fc6879b
JM
54{
55 char *title = "PMK Name";
56 const u8 *addr[3];
57 const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
56586197 58 unsigned char hash[SHA256_MAC_LEN];
6fc6879b
JM
59
60 addr[0] = (u8 *) title;
61 addr[1] = aa;
62 addr[2] = spa;
63
56586197
JM
64#ifdef CONFIG_IEEE80211W
65 if (use_sha256)
66 hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash);
67 else
68#endif /* CONFIG_IEEE80211W */
69 hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash);
6fc6879b
JM
70 os_memcpy(pmkid, hash, PMKID_LEN);
71}
72
73
74static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
75
76
77static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
78{
79 os_free(entry);
80}
81
82
83static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
84 struct rsn_pmksa_cache_entry *entry,
85 int replace)
86{
87 pmksa->pmksa_count--;
88 pmksa->free_cb(entry, pmksa->ctx, replace);
89 _pmksa_cache_free_entry(entry);
90}
91
92
93static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
94{
95 struct rsn_pmksa_cache *pmksa = eloop_ctx;
96 struct os_time now;
97
98 os_get_time(&now);
99 while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
100 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
101 pmksa->pmksa = entry->next;
102 wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
103 MACSTR, MAC2STR(entry->aa));
104 pmksa_cache_free_entry(pmksa, entry, 0);
105 }
106
107 pmksa_cache_set_expiration(pmksa);
108}
109
110
111static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
112{
113 struct rsn_pmksa_cache *pmksa = eloop_ctx;
114 pmksa->sm->cur_pmksa = NULL;
115 eapol_sm_request_reauth(pmksa->sm->eapol);
116}
117
118
119static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
120{
121 int sec;
122 struct rsn_pmksa_cache_entry *entry;
123 struct os_time now;
124
125 eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
126 eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL);
127 if (pmksa->pmksa == NULL)
128 return;
129 os_get_time(&now);
130 sec = pmksa->pmksa->expiration - now.sec;
131 if (sec < 0)
132 sec = 0;
133 eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
134
135 entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
136 pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL);
137 if (entry) {
138 sec = pmksa->pmksa->reauth_time - now.sec;
139 if (sec < 0)
140 sec = 0;
141 eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa,
142 NULL);
143 }
144}
145
146
147/**
148 * pmksa_cache_add - Add a PMKSA cache entry
149 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
150 * @pmk: The new pairwise master key
151 * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
152 * @aa: Authenticator address
153 * @spa: Supplicant address
154 * @network_ctx: Network configuration context for this PMK
56586197 155 * @akmp: WPA_KEY_MGMT_* used in key derivation
6fc6879b
JM
156 * Returns: Pointer to the added PMKSA cache entry or %NULL on error
157 *
158 * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
159 * cache. If an old entry is already in the cache for the same Authenticator,
160 * this entry will be replaced with the new entry. PMKID will be calculated
161 * based on the PMK and the driver interface is notified of the new PMKID.
162 */
163struct rsn_pmksa_cache_entry *
164pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
56586197 165 const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
6fc6879b
JM
166{
167 struct rsn_pmksa_cache_entry *entry, *pos, *prev;
168 struct os_time now;
169
170 if (pmksa->sm->proto != WPA_PROTO_RSN || pmk_len > PMK_LEN)
171 return NULL;
172
173 entry = os_zalloc(sizeof(*entry));
174 if (entry == NULL)
175 return NULL;
176 os_memcpy(entry->pmk, pmk, pmk_len);
177 entry->pmk_len = pmk_len;
56586197
JM
178 rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
179 wpa_key_mgmt_sha256(akmp));
6fc6879b
JM
180 os_get_time(&now);
181 entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
182 entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
183 pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
56586197 184 entry->akmp = akmp;
6fc6879b
JM
185 os_memcpy(entry->aa, aa, ETH_ALEN);
186 entry->network_ctx = network_ctx;
187
188 /* Replace an old entry for the same Authenticator (if found) with the
189 * new entry */
190 pos = pmksa->pmksa;
191 prev = NULL;
192 while (pos) {
193 if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
194 if (pos->pmk_len == pmk_len &&
195 os_memcmp(pos->pmk, pmk, pmk_len) == 0 &&
196 os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) ==
197 0) {
198 wpa_printf(MSG_DEBUG, "WPA: reusing previous "
199 "PMKSA entry");
200 os_free(entry);
201 return pos;
202 }
203 if (prev == NULL)
204 pmksa->pmksa = pos->next;
205 else
206 prev->next = pos->next;
207 if (pos == pmksa->sm->cur_pmksa) {
208 /* We are about to replace the current PMKSA
209 * cache entry. This happens when the PMKSA
210 * caching attempt fails, so we don't want to
211 * force pmksa_cache_free_entry() to disconnect
212 * at this point. Let's just make sure the old
213 * PMKSA cache entry will not be used in the
214 * future.
215 */
216 wpa_printf(MSG_DEBUG, "RSN: replacing current "
217 "PMKSA entry");
218 pmksa->sm->cur_pmksa = NULL;
219 }
220 wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
221 "the current AP");
222 pmksa_cache_free_entry(pmksa, pos, 1);
223 break;
224 }
225 prev = pos;
226 pos = pos->next;
227 }
228
229 if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
230 /* Remove the oldest entry to make room for the new entry */
231 pos = pmksa->pmksa;
232 pmksa->pmksa = pos->next;
233 wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
234 "entry (for " MACSTR ") to make room for new one",
235 MAC2STR(pos->aa));
236 wpa_sm_remove_pmkid(pmksa->sm, pos->aa, pos->pmkid);
237 pmksa_cache_free_entry(pmksa, pos, 0);
238 }
239
240 /* Add the new entry; order by expiration time */
241 pos = pmksa->pmksa;
242 prev = NULL;
243 while (pos) {
244 if (pos->expiration > entry->expiration)
245 break;
246 prev = pos;
247 pos = pos->next;
248 }
249 if (prev == NULL) {
250 entry->next = pmksa->pmksa;
251 pmksa->pmksa = entry;
252 pmksa_cache_set_expiration(pmksa);
253 } else {
254 entry->next = prev->next;
255 prev->next = entry;
256 }
257 pmksa->pmksa_count++;
258 wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
259 MAC2STR(entry->aa));
260 wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid);
261
262 return entry;
263}
264
265
266/**
267 * pmksa_cache_deinit - Free all entries in PMKSA cache
268 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
269 */
270void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
271{
272 struct rsn_pmksa_cache_entry *entry, *prev;
273
274 if (pmksa == NULL)
275 return;
276
277 entry = pmksa->pmksa;
278 pmksa->pmksa = NULL;
279 while (entry) {
280 prev = entry;
281 entry = entry->next;
282 os_free(prev);
283 }
284 pmksa_cache_set_expiration(pmksa);
285 os_free(pmksa);
286}
287
288
289/**
290 * pmksa_cache_get - Fetch a PMKSA cache entry
291 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
292 * @aa: Authenticator address or %NULL to match any
293 * @pmkid: PMKID or %NULL to match any
294 * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
295 */
296struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
297 const u8 *aa, const u8 *pmkid)
298{
299 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
300 while (entry) {
301 if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
302 (pmkid == NULL ||
303 os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
304 return entry;
305 entry = entry->next;
306 }
307 return NULL;
308}
309
310
311/**
312 * pmksa_cache_notify_reconfig - Reconfiguration notification for PMKSA cache
313 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
314 *
315 * Clear references to old data structures when wpa_supplicant is reconfigured.
316 */
317void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
318{
319 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
320 while (entry) {
321 entry->network_ctx = NULL;
322 entry = entry->next;
323 }
324}
325
326
327static struct rsn_pmksa_cache_entry *
328pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
329 const struct rsn_pmksa_cache_entry *old_entry,
330 const u8 *aa)
331{
332 struct rsn_pmksa_cache_entry *new_entry;
333
334 new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
335 aa, pmksa->sm->own_addr,
56586197 336 old_entry->network_ctx, old_entry->akmp);
6fc6879b
JM
337 if (new_entry == NULL)
338 return NULL;
339
340 /* TODO: reorder entries based on expiration time? */
341 new_entry->expiration = old_entry->expiration;
342 new_entry->opportunistic = 1;
343
344 return new_entry;
345}
346
347
348/**
349 * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry
350 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
351 * @network_ctx: Network configuration context
352 * @aa: Authenticator address for the new AP
353 * Returns: Pointer to a new PMKSA cache entry or %NULL if not available
354 *
355 * Try to create a new PMKSA cache entry opportunistically by guessing that the
356 * new AP is sharing the same PMK as another AP that has the same SSID and has
357 * already an entry in PMKSA cache.
358 */
359struct rsn_pmksa_cache_entry *
360pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
361 const u8 *aa)
362{
363 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
364
365 if (network_ctx == NULL)
366 return NULL;
367 while (entry) {
368 if (entry->network_ctx == network_ctx) {
369 entry = pmksa_cache_clone_entry(pmksa, entry, aa);
370 if (entry) {
371 wpa_printf(MSG_DEBUG, "RSN: added "
372 "opportunistic PMKSA cache entry "
373 "for " MACSTR, MAC2STR(aa));
374 }
375 return entry;
376 }
377 entry = entry->next;
378 }
379 return NULL;
380}
381
382
383/**
384 * pmksa_cache_get_current - Get the current used PMKSA entry
385 * @sm: Pointer to WPA state machine data from wpa_sm_init()
386 * Returns: Pointer to the current PMKSA cache entry or %NULL if not available
387 */
388struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm)
389{
390 if (sm == NULL)
391 return NULL;
392 return sm->cur_pmksa;
393}
394
395
396/**
397 * pmksa_cache_clear_current - Clear the current PMKSA entry selection
398 * @sm: Pointer to WPA state machine data from wpa_sm_init()
399 */
400void pmksa_cache_clear_current(struct wpa_sm *sm)
401{
402 if (sm == NULL)
403 return;
404 sm->cur_pmksa = NULL;
405}
406
407
408/**
409 * pmksa_cache_set_current - Set the current PMKSA entry selection
410 * @sm: Pointer to WPA state machine data from wpa_sm_init()
411 * @pmkid: PMKID for selecting PMKSA or %NULL if not used
412 * @bssid: BSSID for PMKSA or %NULL if not used
413 * @network_ctx: Network configuration context
414 * @try_opportunistic: Whether to allow opportunistic PMKSA caching
415 * Returns: 0 if PMKSA was found or -1 if no matching entry was found
416 */
417int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
418 const u8 *bssid, void *network_ctx,
419 int try_opportunistic)
420{
421 struct rsn_pmksa_cache *pmksa = sm->pmksa;
422 sm->cur_pmksa = NULL;
423 if (pmkid)
424 sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid);
425 if (sm->cur_pmksa == NULL && bssid)
426 sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL);
427 if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
428 sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
429 network_ctx,
430 bssid);
431 if (sm->cur_pmksa) {
432 wpa_hexdump(MSG_DEBUG, "RSN: PMKID",
433 sm->cur_pmksa->pmkid, PMKID_LEN);
434 return 0;
435 }
436 return -1;
437}
438
439
440/**
441 * pmksa_cache_list - Dump text list of entries in PMKSA cache
442 * @sm: Pointer to WPA state machine data from wpa_sm_init()
443 * @buf: Buffer for the list
444 * @len: Length of the buffer
445 * Returns: number of bytes written to buffer
446 *
447 * This function is used to generate a text format representation of the
448 * current PMKSA cache contents for the ctrl_iface PMKSA command.
449 */
450int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len)
451{
452 int i, ret;
453 char *pos = buf;
454 struct rsn_pmksa_cache_entry *entry;
455 struct os_time now;
456
457 os_get_time(&now);
458 ret = os_snprintf(pos, buf + len - pos,
459 "Index / AA / PMKID / expiration (in seconds) / "
460 "opportunistic\n");
461 if (ret < 0 || ret >= buf + len - pos)
462 return pos - buf;
463 pos += ret;
464 i = 0;
465 entry = sm->pmksa->pmksa;
466 while (entry) {
467 i++;
468 ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
469 i, MAC2STR(entry->aa));
470 if (ret < 0 || ret >= buf + len - pos)
471 return pos - buf;
472 pos += ret;
473 pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
474 PMKID_LEN);
475 ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
476 (int) (entry->expiration - now.sec),
477 entry->opportunistic);
478 if (ret < 0 || ret >= buf + len - pos)
479 return pos - buf;
480 pos += ret;
481 entry = entry->next;
482 }
483 return pos - buf;
484}
485
486
487/**
488 * pmksa_cache_init - Initialize PMKSA cache
489 * @free_cb: Callback function to be called when a PMKSA cache entry is freed
490 * @ctx: Context pointer for free_cb function
491 * @sm: Pointer to WPA state machine data from wpa_sm_init()
492 * Returns: Pointer to PMKSA cache data or %NULL on failure
493 */
494struct rsn_pmksa_cache *
495pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
496 void *ctx, int replace),
497 void *ctx, struct wpa_sm *sm)
498{
499 struct rsn_pmksa_cache *pmksa;
500
501 pmksa = os_zalloc(sizeof(*pmksa));
502 if (pmksa) {
503 pmksa->free_cb = free_cb;
504 pmksa->ctx = ctx;
505 pmksa->sm = sm;
506 }
507
508 return pmksa;
509}
510
511#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */