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

« back to all changes in this revision

Viewing changes to pmksa_cache.c

  • Committer: Bazaar Package Importer
  • Author(s): Reinhard Tartler
  • Date: 2007-08-26 16:06:57 UTC
  • mto: This revision was merged to the branch mainline in revision 26.
  • Revision ID: james.westby@ubuntu.com-20070826160657-mxk5ivjjh65ptxlr
Tags: upstream-0.6.0+0.5.8
ImportĀ upstreamĀ versionĀ 0.6.0+0.5.8

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 "l2_packet.h"
 
24
#include "eapol_sm.h"
 
25
#include "pmksa_cache.h"
 
26
 
 
27
#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
 
28
 
 
29
static const int pmksa_cache_max_entries = 32;
 
30
 
 
31
struct rsn_pmksa_cache {
 
32
        struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */
 
33
        int pmksa_count; /* number of entries in PMKSA cache */
 
34
        struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
 
35
 
 
36
        void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
 
37
                        int replace);
 
38
        void *ctx;
 
39
};
 
40
 
 
41
 
 
42
/**
 
43
 * rsn_pmkid - Calculate PMK identifier
 
44
 * @pmk: Pairwise master key
 
45
 * @pmk_len: Length of pmk in bytes
 
46
 * @aa: Authenticator address
 
47
 * @spa: Supplicant address
 
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
 */
 
52
static void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa,
 
53
                      const u8 *spa, u8 *pmkid)
 
54
{
 
55
        char *title = "PMK Name";
 
56
        const u8 *addr[3];
 
57
        const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
 
58
        unsigned char hash[SHA1_MAC_LEN];
 
59
 
 
60
        addr[0] = (u8 *) title;
 
61
        addr[1] = aa;
 
62
        addr[2] = spa;
 
63
 
 
64
        hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash);
 
65
        os_memcpy(pmkid, hash, PMKID_LEN);
 
66
}
 
67
 
 
68
 
 
69
static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
 
70
 
 
71
 
 
72
static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
 
73
{
 
74
        os_free(entry);
 
75
}
 
76
 
 
77
 
 
78
static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
 
79
                                   struct rsn_pmksa_cache_entry *entry,
 
80
                                   int replace)
 
81
{
 
82
        pmksa->pmksa_count--;
 
83
        pmksa->free_cb(entry, pmksa->ctx, replace);
 
84
        _pmksa_cache_free_entry(entry);
 
85
}
 
86
 
 
87
 
 
88
static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
 
89
{
 
90
        struct rsn_pmksa_cache *pmksa = eloop_ctx;
 
91
        struct os_time now;
 
92
 
 
93
        os_get_time(&now);
 
94
        while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
 
95
                struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
 
96
                pmksa->pmksa = entry->next;
 
97
                wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
 
98
                           MACSTR, MAC2STR(entry->aa));
 
99
                pmksa_cache_free_entry(pmksa, entry, 0);
 
100
        }
 
101
 
 
102
        pmksa_cache_set_expiration(pmksa);
 
103
}
 
104
 
 
105
 
 
106
static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
 
107
{
 
108
        struct rsn_pmksa_cache *pmksa = eloop_ctx;
 
109
        pmksa->sm->cur_pmksa = NULL;
 
110
        eapol_sm_request_reauth(pmksa->sm->eapol);
 
111
}
 
112
 
 
113
 
 
114
static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
 
115
{
 
116
        int sec;
 
117
        struct rsn_pmksa_cache_entry *entry;
 
118
        struct os_time now;
 
119
 
 
120
        eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
 
121
        eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL);
 
122
        if (pmksa->pmksa == NULL)
 
123
                return;
 
124
        os_get_time(&now);
 
125
        sec = pmksa->pmksa->expiration - now.sec;
 
126
        if (sec < 0)
 
127
                sec = 0;
 
128
        eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
 
129
 
 
130
        entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
 
131
                pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL);
 
132
        if (entry) {
 
133
                sec = pmksa->pmksa->reauth_time - now.sec;
 
134
                if (sec < 0)
 
135
                        sec = 0;
 
136
                eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa,
 
137
                                       NULL);
 
138
        }
 
139
}
 
140
 
 
141
 
 
142
/**
 
143
 * pmksa_cache_add - Add a PMKSA cache entry
 
144
 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
 
145
 * @pmk: The new pairwise master key
 
146
 * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
 
147
 * @aa: Authenticator address
 
148
 * @spa: Supplicant address
 
149
 * @ssid: The network configuration for which this PMK is being added
 
150
 * Returns: Pointer to the added PMKSA cache entry or %NULL on error
 
151
 *
 
152
 * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
 
153
 * cache. If an old entry is already in the cache for the same Authenticator,
 
154
 * this entry will be replaced with the new entry. PMKID will be calculated
 
155
 * based on the PMK and the driver interface is notified of the new PMKID.
 
156
 */
 
157
struct rsn_pmksa_cache_entry *
 
158
pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
 
159
                const u8 *aa, const u8 *spa, struct wpa_ssid *ssid)
 
160
{
 
161
        struct rsn_pmksa_cache_entry *entry, *pos, *prev;
 
162
        struct os_time now;
 
163
 
 
164
        if (pmksa->sm->proto != WPA_PROTO_RSN || pmk_len > PMK_LEN)
 
165
                return NULL;
 
166
 
 
167
        entry = os_zalloc(sizeof(*entry));
 
168
        if (entry == NULL)
 
169
                return NULL;
 
170
        os_memcpy(entry->pmk, pmk, pmk_len);
 
171
        entry->pmk_len = pmk_len;
 
172
        rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid);
 
173
        os_get_time(&now);
 
174
        entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
 
175
        entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
 
176
                pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
 
177
        entry->akmp = WPA_KEY_MGMT_IEEE8021X;
 
178
        os_memcpy(entry->aa, aa, ETH_ALEN);
 
179
        entry->ssid = ssid;
 
180
 
 
181
        /* Replace an old entry for the same Authenticator (if found) with the
 
182
         * new entry */
 
183
        pos = pmksa->pmksa;
 
184
        prev = NULL;
 
185
        while (pos) {
 
186
                if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
 
187
                        if (pos->pmk_len == pmk_len &&
 
188
                            os_memcmp(pos->pmk, pmk, pmk_len) == 0 &&
 
189
                            os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) ==
 
190
                            0) {
 
191
                                wpa_printf(MSG_DEBUG, "WPA: reusing previous "
 
192
                                           "PMKSA entry");
 
193
                                os_free(entry);
 
194
                                return pos;
 
195
                        }
 
196
                        if (prev == NULL)
 
197
                                pmksa->pmksa = pos->next;
 
198
                        else
 
199
                                prev->next = pos->next;
 
200
                        if (pos == pmksa->sm->cur_pmksa) {
 
201
                                /* We are about to replace the current PMKSA
 
202
                                 * cache entry. This happens when the PMKSA
 
203
                                 * caching attempt fails, so we don't want to
 
204
                                 * force pmksa_cache_free_entry() to disconnect
 
205
                                 * at this point. Let's just make sure the old
 
206
                                 * PMKSA cache entry will not be used in the
 
207
                                 * future.
 
208
                                 */
 
209
                                wpa_printf(MSG_DEBUG, "RSN: replacing current "
 
210
                                           "PMKSA entry");
 
211
                                pmksa->sm->cur_pmksa = NULL;
 
212
                        }
 
213
                        wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
 
214
                                   "the current AP");
 
215
                        pmksa_cache_free_entry(pmksa, pos, 1);
 
216
                        break;
 
217
                }
 
218
                prev = pos;
 
219
                pos = pos->next;
 
220
        }
 
221
 
 
222
        if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
 
223
                /* Remove the oldest entry to make room for the new entry */
 
224
                pos = pmksa->pmksa;
 
225
                pmksa->pmksa = pos->next;
 
226
                wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
 
227
                           "entry (for " MACSTR ") to make room for new one",
 
228
                           MAC2STR(pos->aa));
 
229
                wpa_sm_remove_pmkid(pmksa->sm, pos->aa, pos->pmkid);
 
230
                pmksa_cache_free_entry(pmksa, pos, 0);
 
231
        }
 
232
 
 
233
        /* Add the new entry; order by expiration time */
 
234
        pos = pmksa->pmksa;
 
235
        prev = NULL;
 
236
        while (pos) {
 
237
                if (pos->expiration > entry->expiration)
 
238
                        break;
 
239
                prev = pos;
 
240
                pos = pos->next;
 
241
        }
 
242
        if (prev == NULL) {
 
243
                entry->next = pmksa->pmksa;
 
244
                pmksa->pmksa = entry;
 
245
                pmksa_cache_set_expiration(pmksa);
 
246
        } else {
 
247
                entry->next = prev->next;
 
248
                prev->next = entry;
 
249
        }
 
250
        pmksa->pmksa_count++;
 
251
        wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
 
252
                   MAC2STR(entry->aa));
 
253
        wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid);
 
254
 
 
255
        return entry;
 
256
}
 
257
 
 
258
 
 
259
/**
 
260
 * pmksa_cache_deinit - Free all entries in PMKSA cache
 
261
 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
 
262
 */
 
263
void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
 
264
{
 
265
        struct rsn_pmksa_cache_entry *entry, *prev;
 
266
 
 
267
        if (pmksa == NULL)
 
268
                return;
 
269
 
 
270
        entry = pmksa->pmksa;
 
271
        pmksa->pmksa = NULL;
 
272
        while (entry) {
 
273
                prev = entry;
 
274
                entry = entry->next;
 
275
                os_free(prev);
 
276
        }
 
277
        pmksa_cache_set_expiration(pmksa);
 
278
        os_free(pmksa);
 
279
}
 
280
 
 
281
 
 
282
/**
 
283
 * pmksa_cache_get - Fetch a PMKSA cache entry
 
284
 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
 
285
 * @aa: Authenticator address or %NULL to match any
 
286
 * @pmkid: PMKID or %NULL to match any
 
287
 * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
 
288
 */
 
289
struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
 
290
                                               const u8 *aa, const u8 *pmkid)
 
291
{
 
292
        struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
 
293
        while (entry) {
 
294
                if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
 
295
                    (pmkid == NULL ||
 
296
                     os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
 
297
                        return entry;
 
298
                entry = entry->next;
 
299
        }
 
300
        return NULL;
 
301
}
 
302
 
 
303
 
 
304
/**
 
305
 * pmksa_cache_notify_reconfig - Reconfiguration notification for PMKSA cache
 
306
 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
 
307
 *
 
308
 * Clear references to old data structures when wpa_supplicant is reconfigured.
 
309
 */
 
310
void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
 
311
{
 
312
        struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
 
313
        while (entry) {
 
314
                entry->ssid = NULL;
 
315
                entry = entry->next;
 
316
        }
 
317
}
 
318
 
 
319
 
 
320
static struct rsn_pmksa_cache_entry *
 
321
pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
 
322
                        const struct rsn_pmksa_cache_entry *old_entry,
 
323
                        const u8 *aa)
 
324
{
 
325
        struct rsn_pmksa_cache_entry *new_entry;
 
326
 
 
327
        new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
 
328
                                    aa, pmksa->sm->own_addr, old_entry->ssid);
 
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
 * @ssid: Pointer to the current network configuration
 
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,
 
353
                              struct wpa_ssid *ssid, const u8 *aa)
 
354
{
 
355
        struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
 
356
 
 
357
        if (ssid == NULL)
 
358
                return NULL;
 
359
        while (entry) {
 
360
                if (entry->ssid == ssid) {
 
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
 * @ssid: The network configuration for the current network
 
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, struct wpa_ssid *ssid,
 
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, ssid,
 
421
                                                              bssid);
 
422
        if (sm->cur_pmksa) {
 
423
                wpa_hexdump(MSG_DEBUG, "RSN: PMKID",
 
424
                            sm->cur_pmksa->pmkid, PMKID_LEN);
 
425
                return 0;
 
426
        }
 
427
        return -1;
 
428
}
 
429
 
 
430
 
 
431
/**
 
432
 * pmksa_cache_list - Dump text list of entries in PMKSA cache
 
433
 * @sm: Pointer to WPA state machine data from wpa_sm_init()
 
434
 * @buf: Buffer for the list
 
435
 * @len: Length of the buffer
 
436
 * Returns: number of bytes written to buffer
 
437
 *
 
438
 * This function is used to generate a text format representation of the
 
439
 * current PMKSA cache contents for the ctrl_iface PMKSA command.
 
440
 */
 
441
int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len)
 
442
{
 
443
        int i, ret;
 
444
        char *pos = buf;
 
445
        struct rsn_pmksa_cache_entry *entry;
 
446
        struct os_time now;
 
447
 
 
448
        os_get_time(&now);
 
449
        ret = os_snprintf(pos, buf + len - pos,
 
450
                          "Index / AA / PMKID / expiration (in seconds) / "
 
451
                          "opportunistic\n");
 
452
        if (ret < 0 || ret >= buf + len - pos)
 
453
                return pos - buf;
 
454
        pos += ret;
 
455
        i = 0;
 
456
        entry = sm->pmksa->pmksa;
 
457
        while (entry) {
 
458
                i++;
 
459
                ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
 
460
                                  i, MAC2STR(entry->aa));
 
461
                if (ret < 0 || ret >= buf + len - pos)
 
462
                        return pos - buf;
 
463
                pos += ret;
 
464
                pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
 
465
                                        PMKID_LEN);
 
466
                ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
 
467
                                  (int) (entry->expiration - now.sec),
 
468
                                  entry->opportunistic);
 
469
                if (ret < 0 || ret >= buf + len - pos)
 
470
                        return pos - buf;
 
471
                pos += ret;
 
472
                entry = entry->next;
 
473
        }
 
474
        return pos - buf;
 
475
}
 
476
 
 
477
 
 
478
/**
 
479
 * pmksa_cache_init - Initialize PMKSA cache
 
480
 * @free_cb: Callback function to be called when a PMKSA cache entry is freed
 
481
 * @ctx: Context pointer for free_cb function
 
482
 * @sm: Pointer to WPA state machine data from wpa_sm_init()
 
483
 * Returns: Pointer to PMKSA cache data or %NULL on failure
 
484
 */
 
485
struct rsn_pmksa_cache *
 
486
pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
 
487
                                 void *ctx, int replace),
 
488
                 void *ctx, struct wpa_sm *sm)
 
489
{
 
490
        struct rsn_pmksa_cache *pmksa;
 
491
 
 
492
        pmksa = os_zalloc(sizeof(*pmksa));
 
493
        if (pmksa) {
 
494
                pmksa->free_cb = free_cb;
 
495
                pmksa->ctx = ctx;
 
496
                pmksa->sm = sm;
 
497
        }
 
498
 
 
499
        return pmksa;
 
500
}
 
501
 
 
502
#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */