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