pmksa_cache_auth.c revision 214501
1/*
2 * hostapd - PMKSA cache for IEEE 802.11i RSN
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 "utils/includes.h"
16
17#include "utils/common.h"
18#include "utils/eloop.h"
19#include "eapol_auth/eapol_auth_sm.h"
20#include "eapol_auth/eapol_auth_sm_i.h"
21#include "sta_info.h"
22#include "ap_config.h"
23#include "pmksa_cache_auth.h"
24
25
26static const int pmksa_cache_max_entries = 1024;
27static const int dot11RSNAConfigPMKLifetime = 43200;
28
29struct rsn_pmksa_cache {
30#define PMKID_HASH_SIZE 128
31#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f)
32	struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE];
33	struct rsn_pmksa_cache_entry *pmksa;
34	int pmksa_count;
35
36	void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
37	void *ctx;
38};
39
40
41static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
42
43
44static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
45{
46	if (entry == NULL)
47		return;
48	os_free(entry->identity);
49#ifndef CONFIG_NO_RADIUS
50	radius_free_class(&entry->radius_class);
51#endif /* CONFIG_NO_RADIUS */
52	os_free(entry);
53}
54
55
56static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
57				   struct rsn_pmksa_cache_entry *entry)
58{
59	struct rsn_pmksa_cache_entry *pos, *prev;
60
61	pmksa->pmksa_count--;
62	pmksa->free_cb(entry, pmksa->ctx);
63	pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
64	prev = NULL;
65	while (pos) {
66		if (pos == entry) {
67			if (prev != NULL) {
68				prev->hnext = pos->hnext;
69			} else {
70				pmksa->pmkid[PMKID_HASH(entry->pmkid)] =
71					pos->hnext;
72			}
73			break;
74		}
75		prev = pos;
76		pos = pos->hnext;
77	}
78
79	pos = pmksa->pmksa;
80	prev = NULL;
81	while (pos) {
82		if (pos == entry) {
83			if (prev != NULL)
84				prev->next = pos->next;
85			else
86				pmksa->pmksa = pos->next;
87			break;
88		}
89		prev = pos;
90		pos = pos->next;
91	}
92	_pmksa_cache_free_entry(entry);
93}
94
95
96static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
97{
98	struct rsn_pmksa_cache *pmksa = eloop_ctx;
99	struct os_time now;
100
101	os_get_time(&now);
102	while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
103		struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
104		pmksa->pmksa = entry->next;
105		wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
106			   MACSTR, MAC2STR(entry->spa));
107		pmksa_cache_free_entry(pmksa, entry);
108	}
109
110	pmksa_cache_set_expiration(pmksa);
111}
112
113
114static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
115{
116	int sec;
117	struct os_time now;
118
119	eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
120	if (pmksa->pmksa == NULL)
121		return;
122	os_get_time(&now);
123	sec = pmksa->pmksa->expiration - now.sec;
124	if (sec < 0)
125		sec = 0;
126	eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
127}
128
129
130static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
131					struct eapol_state_machine *eapol)
132{
133	if (eapol == NULL)
134		return;
135
136	if (eapol->identity) {
137		entry->identity = os_malloc(eapol->identity_len);
138		if (entry->identity) {
139			entry->identity_len = eapol->identity_len;
140			os_memcpy(entry->identity, eapol->identity,
141				  eapol->identity_len);
142		}
143	}
144
145#ifndef CONFIG_NO_RADIUS
146	radius_copy_class(&entry->radius_class, &eapol->radius_class);
147#endif /* CONFIG_NO_RADIUS */
148
149	entry->eap_type_authsrv = eapol->eap_type_authsrv;
150	entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id;
151}
152
153
154void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
155			       struct eapol_state_machine *eapol)
156{
157	if (entry == NULL || eapol == NULL)
158		return;
159
160	if (entry->identity) {
161		os_free(eapol->identity);
162		eapol->identity = os_malloc(entry->identity_len);
163		if (eapol->identity) {
164			eapol->identity_len = entry->identity_len;
165			os_memcpy(eapol->identity, entry->identity,
166				  entry->identity_len);
167		}
168		wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA",
169				  eapol->identity, eapol->identity_len);
170	}
171
172#ifndef CONFIG_NO_RADIUS
173	radius_free_class(&eapol->radius_class);
174	radius_copy_class(&eapol->radius_class, &entry->radius_class);
175#endif /* CONFIG_NO_RADIUS */
176	if (eapol->radius_class.attr) {
177		wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from "
178			   "PMKSA", (unsigned long) eapol->radius_class.count);
179	}
180
181	eapol->eap_type_authsrv = entry->eap_type_authsrv;
182	((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id;
183}
184
185
186static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
187				   struct rsn_pmksa_cache_entry *entry)
188{
189	struct rsn_pmksa_cache_entry *pos, *prev;
190
191	/* Add the new entry; order by expiration time */
192	pos = pmksa->pmksa;
193	prev = NULL;
194	while (pos) {
195		if (pos->expiration > entry->expiration)
196			break;
197		prev = pos;
198		pos = pos->next;
199	}
200	if (prev == NULL) {
201		entry->next = pmksa->pmksa;
202		pmksa->pmksa = entry;
203	} else {
204		entry->next = prev->next;
205		prev->next = entry;
206	}
207	entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
208	pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry;
209
210	pmksa->pmksa_count++;
211	wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
212		   MAC2STR(entry->spa));
213	wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
214}
215
216
217/**
218 * pmksa_cache_auth_add - Add a PMKSA cache entry
219 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
220 * @pmk: The new pairwise master key
221 * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
222 * @aa: Authenticator address
223 * @spa: Supplicant address
224 * @session_timeout: Session timeout
225 * @eapol: Pointer to EAPOL state machine data
226 * @akmp: WPA_KEY_MGMT_* used in key derivation
227 * Returns: Pointer to the added PMKSA cache entry or %NULL on error
228 *
229 * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
230 * cache. If an old entry is already in the cache for the same Supplicant,
231 * this entry will be replaced with the new entry. PMKID will be calculated
232 * based on the PMK.
233 */
234struct rsn_pmksa_cache_entry *
235pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
236		     const u8 *pmk, size_t pmk_len,
237		const u8 *aa, const u8 *spa, int session_timeout,
238		struct eapol_state_machine *eapol, int akmp)
239{
240	struct rsn_pmksa_cache_entry *entry, *pos;
241	struct os_time now;
242
243	if (pmk_len > PMK_LEN)
244		return NULL;
245
246	entry = os_zalloc(sizeof(*entry));
247	if (entry == NULL)
248		return NULL;
249	os_memcpy(entry->pmk, pmk, pmk_len);
250	entry->pmk_len = pmk_len;
251	rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
252		  wpa_key_mgmt_sha256(akmp));
253	os_get_time(&now);
254	entry->expiration = now.sec;
255	if (session_timeout > 0)
256		entry->expiration += session_timeout;
257	else
258		entry->expiration += dot11RSNAConfigPMKLifetime;
259	entry->akmp = akmp;
260	os_memcpy(entry->spa, spa, ETH_ALEN);
261	pmksa_cache_from_eapol_data(entry, eapol);
262
263	/* Replace an old entry for the same STA (if found) with the new entry
264	 */
265	pos = pmksa_cache_auth_get(pmksa, spa, NULL);
266	if (pos)
267		pmksa_cache_free_entry(pmksa, pos);
268
269	if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
270		/* Remove the oldest entry to make room for the new entry */
271		wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
272			   "entry (for " MACSTR ") to make room for new one",
273			   MAC2STR(pmksa->pmksa->spa));
274		pmksa_cache_free_entry(pmksa, pmksa->pmksa);
275	}
276
277	pmksa_cache_link_entry(pmksa, entry);
278
279	return entry;
280}
281
282
283struct rsn_pmksa_cache_entry *
284pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
285		    const struct rsn_pmksa_cache_entry *old_entry,
286		    const u8 *aa, const u8 *pmkid)
287{
288	struct rsn_pmksa_cache_entry *entry;
289
290	entry = os_zalloc(sizeof(*entry));
291	if (entry == NULL)
292		return NULL;
293	os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
294	os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len);
295	entry->pmk_len = old_entry->pmk_len;
296	entry->expiration = old_entry->expiration;
297	entry->akmp = old_entry->akmp;
298	os_memcpy(entry->spa, old_entry->spa, ETH_ALEN);
299	entry->opportunistic = 1;
300	if (old_entry->identity) {
301		entry->identity = os_malloc(old_entry->identity_len);
302		if (entry->identity) {
303			entry->identity_len = old_entry->identity_len;
304			os_memcpy(entry->identity, old_entry->identity,
305				  old_entry->identity_len);
306		}
307	}
308#ifndef CONFIG_NO_RADIUS
309	radius_copy_class(&entry->radius_class, &old_entry->radius_class);
310#endif /* CONFIG_NO_RADIUS */
311	entry->eap_type_authsrv = old_entry->eap_type_authsrv;
312	entry->vlan_id = old_entry->vlan_id;
313	entry->opportunistic = 1;
314
315	pmksa_cache_link_entry(pmksa, entry);
316
317	return entry;
318}
319
320
321/**
322 * pmksa_cache_auth_deinit - Free all entries in PMKSA cache
323 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
324 */
325void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
326{
327	struct rsn_pmksa_cache_entry *entry, *prev;
328	int i;
329
330	if (pmksa == NULL)
331		return;
332
333	entry = pmksa->pmksa;
334	while (entry) {
335		prev = entry;
336		entry = entry->next;
337		_pmksa_cache_free_entry(prev);
338	}
339	eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
340	for (i = 0; i < PMKID_HASH_SIZE; i++)
341		pmksa->pmkid[i] = NULL;
342	os_free(pmksa);
343}
344
345
346/**
347 * pmksa_cache_auth_get - Fetch a PMKSA cache entry
348 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
349 * @spa: Supplicant address or %NULL to match any
350 * @pmkid: PMKID or %NULL to match any
351 * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
352 */
353struct rsn_pmksa_cache_entry *
354pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
355		     const u8 *spa, const u8 *pmkid)
356{
357	struct rsn_pmksa_cache_entry *entry;
358
359	if (pmkid)
360		entry = pmksa->pmkid[PMKID_HASH(pmkid)];
361	else
362		entry = pmksa->pmksa;
363	while (entry) {
364		if ((spa == NULL ||
365		     os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
366		    (pmkid == NULL ||
367		     os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
368			return entry;
369		entry = pmkid ? entry->hnext : entry->next;
370	}
371	return NULL;
372}
373
374
375/**
376 * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC
377 * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
378 * @aa: Authenticator address
379 * @spa: Supplicant address
380 * @pmkid: PMKID
381 * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
382 *
383 * Use opportunistic key caching (OKC) to find a PMK for a supplicant.
384 */
385struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
386	struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
387	const u8 *pmkid)
388{
389	struct rsn_pmksa_cache_entry *entry;
390	u8 new_pmkid[PMKID_LEN];
391
392	entry = pmksa->pmksa;
393	while (entry) {
394		if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
395			continue;
396		rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
397			  wpa_key_mgmt_sha256(entry->akmp));
398		if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
399			return entry;
400		entry = entry->next;
401	}
402	return NULL;
403}
404
405
406/**
407 * pmksa_cache_auth_init - Initialize PMKSA cache
408 * @free_cb: Callback function to be called when a PMKSA cache entry is freed
409 * @ctx: Context pointer for free_cb function
410 * Returns: Pointer to PMKSA cache data or %NULL on failure
411 */
412struct rsn_pmksa_cache *
413pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
414				      void *ctx), void *ctx)
415{
416	struct rsn_pmksa_cache *pmksa;
417
418	pmksa = os_zalloc(sizeof(*pmksa));
419	if (pmksa) {
420		pmksa->free_cb = free_cb;
421		pmksa->ctx = ctx;
422	}
423
424	return pmksa;
425}
426