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