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