hostapd: remove version tag from directory
[dragonfly.git] / contrib / hostapd / pmksa_cache.c
1 /*
2  * hostapd - PMKSA cache for IEEE 802.11i RSN
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 "hostapd.h"
18 #include "common.h"
19 #include "wpa.h"
20 #include "eloop.h"
21 #include "sha1.h"
22 #include "ieee802_1x.h"
23 #include "eapol_sm.h"
24 #include "pmksa_cache.h"
25
26
27 static const int pmksa_cache_max_entries = 1024;
28 static const int dot11RSNAConfigPMKLifetime = 43200;
29
30 struct rsn_pmksa_cache {
31 #define PMKID_HASH_SIZE 128
32 #define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f)
33         struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE];
34         struct rsn_pmksa_cache_entry *pmksa;
35         int pmksa_count;
36
37         void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
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 void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
53                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         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         if (entry == NULL)
75                 return;
76         free(entry->identity);
77         ieee802_1x_free_radius_class(&entry->radius_class);
78         free(entry);
79 }
80
81
82 static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
83                                    struct rsn_pmksa_cache_entry *entry)
84 {
85         struct rsn_pmksa_cache_entry *pos, *prev;
86
87         pmksa->pmksa_count--;
88         pmksa->free_cb(entry, pmksa->ctx);
89         pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
90         prev = NULL;
91         while (pos) {
92                 if (pos == entry) {
93                         if (prev != NULL) {
94                                 prev->hnext = pos->hnext;
95                         } else {
96                                 pmksa->pmkid[PMKID_HASH(entry->pmkid)] =
97                                         pos->hnext;
98                         }
99                         break;
100                 }
101                 prev = pos;
102                 pos = pos->hnext;
103         }
104
105         pos = pmksa->pmksa;
106         prev = NULL;
107         while (pos) {
108                 if (pos == entry) {
109                         if (prev != NULL)
110                                 prev->next = pos->next;
111                         else
112                                 pmksa->pmksa = pos->next;
113                         break;
114                 }
115                 prev = pos;
116                 pos = pos->next;
117         }
118         _pmksa_cache_free_entry(entry);
119 }
120
121
122 static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
123 {
124         struct rsn_pmksa_cache *pmksa = eloop_ctx;
125         struct os_time now;
126
127         os_get_time(&now);
128         while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
129                 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
130                 pmksa->pmksa = entry->next;
131                 wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
132                            MACSTR, MAC2STR(entry->spa));
133                 pmksa_cache_free_entry(pmksa, entry);
134         }
135
136         pmksa_cache_set_expiration(pmksa);
137 }
138
139
140 static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
141 {
142         int sec;
143         struct os_time now;
144
145         eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
146         if (pmksa->pmksa == NULL)
147                 return;
148         os_get_time(&now);
149         sec = pmksa->pmksa->expiration - now.sec;
150         if (sec < 0)
151                 sec = 0;
152         eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
153 }
154
155
156 static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
157                                         struct eapol_state_machine *eapol)
158 {
159         if (eapol == NULL)
160                 return;
161
162         if (eapol->identity) {
163                 entry->identity = malloc(eapol->identity_len);
164                 if (entry->identity) {
165                         entry->identity_len = eapol->identity_len;
166                         memcpy(entry->identity, eapol->identity,
167                                eapol->identity_len);
168                 }
169         }
170
171         ieee802_1x_copy_radius_class(&entry->radius_class,
172                                      &eapol->radius_class);
173
174         entry->eap_type_authsrv = eapol->eap_type_authsrv;
175         entry->vlan_id = eapol->sta->vlan_id;
176 }
177
178
179 void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
180                                struct eapol_state_machine *eapol)
181 {
182         if (entry == NULL || eapol == NULL)
183                 return;
184
185         if (entry->identity) {
186                 free(eapol->identity);
187                 eapol->identity = malloc(entry->identity_len);
188                 if (eapol->identity) {
189                         eapol->identity_len = entry->identity_len;
190                         memcpy(eapol->identity, entry->identity,
191                                entry->identity_len);
192                 }
193                 wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA",
194                                   eapol->identity, eapol->identity_len);
195         }
196
197         ieee802_1x_free_radius_class(&eapol->radius_class);
198         ieee802_1x_copy_radius_class(&eapol->radius_class,
199                                      &entry->radius_class);
200         if (eapol->radius_class.attr) {
201                 wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from "
202                            "PMKSA", (unsigned long) eapol->radius_class.count);
203         }
204
205         eapol->eap_type_authsrv = entry->eap_type_authsrv;
206         eapol->sta->vlan_id = entry->vlan_id;
207 }
208
209
210 /**
211  * pmksa_cache_add - Add a PMKSA cache entry
212  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
213  * @pmk: The new pairwise master key
214  * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
215  * @aa: Authenticator address
216  * @spa: Supplicant address
217  * @session_timeout: Session timeout
218  * @eapol: Pointer to EAPOL state machine data
219  * Returns: Pointer to the added PMKSA cache entry or %NULL on error
220  *
221  * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
222  * cache. If an old entry is already in the cache for the same Supplicant,
223  * this entry will be replaced with the new entry. PMKID will be calculated
224  * based on the PMK.
225  */
226 struct rsn_pmksa_cache_entry *
227 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
228                 const u8 *aa, const u8 *spa, int session_timeout,
229                 struct eapol_state_machine *eapol)
230 {
231         struct rsn_pmksa_cache_entry *entry, *pos, *prev;
232         struct os_time now;
233
234         if (pmk_len > PMK_LEN)
235                 return NULL;
236
237         entry = wpa_zalloc(sizeof(*entry));
238         if (entry == NULL)
239                 return NULL;
240         memcpy(entry->pmk, pmk, pmk_len);
241         entry->pmk_len = pmk_len;
242         rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid);
243         os_get_time(&now);
244         entry->expiration = now.sec;
245         if (session_timeout > 0)
246                 entry->expiration += session_timeout;
247         else
248                 entry->expiration += dot11RSNAConfigPMKLifetime;
249         entry->akmp = WPA_KEY_MGMT_IEEE8021X;
250         memcpy(entry->spa, spa, ETH_ALEN);
251         pmksa_cache_from_eapol_data(entry, eapol);
252
253         /* Replace an old entry for the same STA (if found) with the new entry
254          */
255         pos = pmksa_cache_get(pmksa, spa, NULL);
256         if (pos)
257                 pmksa_cache_free_entry(pmksa, pos);
258
259         if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
260                 /* Remove the oldest entry to make room for the new entry */
261                 wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
262                            "entry (for " MACSTR ") to make room for new one",
263                            MAC2STR(pmksa->pmksa->spa));
264                 pmksa_cache_free_entry(pmksa, pmksa->pmksa);
265         }
266
267         /* Add the new entry; order by expiration time */
268         pos = pmksa->pmksa;
269         prev = NULL;
270         while (pos) {
271                 if (pos->expiration > entry->expiration)
272                         break;
273                 prev = pos;
274                 pos = pos->next;
275         }
276         if (prev == NULL) {
277                 entry->next = pmksa->pmksa;
278                 pmksa->pmksa = entry;
279         } else {
280                 entry->next = prev->next;
281                 prev->next = entry;
282         }
283         entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
284         pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry;
285
286         pmksa->pmksa_count++;
287         wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
288                    MAC2STR(entry->spa));
289         wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
290
291         return entry;
292 }
293
294
295 /**
296  * pmksa_cache_deinit - Free all entries in PMKSA cache
297  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
298  */
299 void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
300 {
301         struct rsn_pmksa_cache_entry *entry, *prev;
302         int i;
303
304         if (pmksa == NULL)
305                 return;
306
307         entry = pmksa->pmksa;
308         while (entry) {
309                 prev = entry;
310                 entry = entry->next;
311                 _pmksa_cache_free_entry(prev);
312         }
313         eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
314         for (i = 0; i < PMKID_HASH_SIZE; i++)
315                 pmksa->pmkid[i] = NULL;
316         free(pmksa);
317 }
318
319
320 /**
321  * pmksa_cache_get - Fetch a PMKSA cache entry
322  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
323  * @spa: Supplicant address or %NULL to match any
324  * @pmkid: PMKID or %NULL to match any
325  * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
326  */
327 struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
328                                                const u8 *spa, const u8 *pmkid)
329 {
330         struct rsn_pmksa_cache_entry *entry;
331
332         if (pmkid)
333                 entry = pmksa->pmkid[PMKID_HASH(pmkid)];
334         else
335                 entry = pmksa->pmksa;
336         while (entry) {
337                 if ((spa == NULL || memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
338                     (pmkid == NULL ||
339                      memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
340                         return entry;
341                 entry = pmkid ? entry->hnext : entry->next;
342         }
343         return NULL;
344 }
345
346
347 /**
348  * pmksa_cache_init - Initialize PMKSA cache
349  * @free_cb: Callback function to be called when a PMKSA cache entry is freed
350  * @ctx: Context pointer for free_cb function
351  * Returns: Pointer to PMKSA cache data or %NULL on failure
352  */
353 struct rsn_pmksa_cache *
354 pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
355                                  void *ctx), void *ctx)
356 {
357         struct rsn_pmksa_cache *pmksa;
358
359         pmksa = wpa_zalloc(sizeof(*pmksa));
360         if (pmksa) {
361                 pmksa->free_cb = free_cb;
362                 pmksa->ctx = ctx;
363         }
364
365         return pmksa;
366 }