~ubuntu-branches/ubuntu/precise/wpasupplicant/precise-proposed

« back to all changes in this revision

Viewing changes to .pc/0007-pmkokc-Clear-OKC-based-PMKSA-caching-entries-if-PMK-is-chan.patch/src/rsn_supp/pmksa_cache.c

  • Committer: Package Import Robot
  • Author(s): Mathieu Trudel-Lapierre
  • Date: 2013-06-12 15:57:50 UTC
  • Revision ID: package-import@ubuntu.com-20130612155750-go9lgylcef8n3f2f
Tags: 0.7.3-6ubuntu2.2
* Multiple patches to reduce the number of disconnections for WPA Enterprise
  roaming and Opportunistic Key Caching. (LP: #1187524)
* In debian/patches:
  0001-sme-fix-retry-after-auth-assoc-timeout-failure.patch,
  0002-sme-optimize-recovery-from-common-load-balancing-mechanisms.patch,
  0003-sme-blacklist-bss-on-first-failure-if-only-a-*.patch,
  0004-sme-extend-load-balancing-optimization-in-bss-blacklisting.patch,
  0005-sme-optimize-recovery-from-association-command-failures.patch,
  0006-sme-add-timers-for-authentication-and-association.patch,
  0007-sme-nl80211-set-cipher-suites.patch:
  Cherry-pick patches fixing SME (Session Management Entity) for the nl80211
  driver, which works as a basis for the OKC patches.
* In debian/patches:
  0001-pmkokc-Set-portValid-TRUE-on-association-for-driver-based-4.patch,
  0002-pmkokc-Clear-WPA-and-EAPOL-state-machine-config-pointer-on-.patch,
  0003-pmkokc-Clear-driver-PMKSA-cache-entry-on-PMKSA-cache-expira.patch,
  0004-pmkokc-Flush-PMKSA-cache-entries-and-invalidate-EAP-state-o.patch,
  0005-pmkokc-Fix-proactive_key_caching-configuration-to-WPA-code.patch,
  0006-pmkokc-RSN-Add-a-debug-message-when-considing-addition-of-O.patch,
  0007-pmkokc-Clear-OKC-based-PMKSA-caching-entries-if-PMK-is-chan.patch,
  0008-pmkokc-Move-wpa_sm_remove_pmkid-call-to-PMKSA-cache-entry-f.patch,
  0009-pmkokc-Use-PMKSA-cache-entries-with-only-a-single-network-c.patch,
  0010-pmkokc-PMKSA-Do-not-evict-active-cache-entry-when-adding-ne.patch,
  0011-pmkokc-PMKSA-Set-cur_pmksa-pointer-during-initial-associati.patch,
  0012-pmkokc-PMKSA-make-deauthentication-due-to-cache-entry-remov.patch,
  0013-pmkokc-PMKSA-update-current-cache-entry-due-to-association-.patch:
  Cherry-pick patches to properly do OKC (Opportunistic Key Caching) which
  helps maintaining connectivity on networks secured with WPA Enterprise,
  especially on nl80211-based drivers -- these patches require SME, and add
  or fix key caching and handling of the cache entries.
* debian/patches/force-enable-okc.patch: force Opportunistic Key Caching to
  be enabled.
* debian/patches/less-aggressive-roaming.patch: use less aggressive roaming
  settings to avoid switching to another AP unnecessarily, when the actual
  signal level difference is small.
* debian/patches/wpa_supplicant-dbus-null-error.patch: Don't send NULL to
  dbus_message_new_error().
* debian/patches/0001-nl80211-Fix-UNSPEC-signal-quality-reporting.patch: fix
  marking qual as invalid rather than signal level.
* debian/patches/wpa_supplicant-squelch-driver-disconnect-spam.patch: recover
  cleanly from streams of disconnect messages (like on iwl3945).
* debian/patches/wpa_supplicant-assoc-timeout.patch: increase association
  timeouts.

Show diffs side-by-side

added added

removed removed

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