~ubuntu-branches/ubuntu/gutsy/wpasupplicant/gutsy

« back to all changes in this revision

Viewing changes to src/rsn_supp/pmksa_cache.c

  • Committer: Bazaar Package Importer
  • Author(s): Reinhard Tartler, Alexander Sack
  • Date: 2007-08-26 16:06:57 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20070826160657-2m8pxoweuxe8f93t
Tags: 0.6.0+0.5.8-0ubuntu1
* New upstream release
* remove patch 11_erroneous_manpage_ref, applied upstream
* remove patch 25_wpas_dbus_unregister_iface_fix, applied upstream

[ Alexander Sack ]
* bumping upstream version to replace development version 0.6.0 with
  this package from stable release branch.
* attempt to fix wierd timeout and high latency issues by going
  back to stable upstream version (0.5.9) (LP: #140763,
  LP: #141233).

Show diffs side-by-side

added added

removed removed

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