• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/wpa_supplicant-0.7.3/src/rsn_supp/
1/*
2 * RSN pre-authentication (supplicant)
3 * Copyright (c) 2003-2010, 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 "l2_packet/l2_packet.h"
21#include "eapol_supp/eapol_supp_sm.h"
22#include "preauth.h"
23#include "pmksa_cache.h"
24#include "wpa_i.h"
25#include "common/ieee802_11_defs.h"
26
27
28#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
29
30#define PMKID_CANDIDATE_PRIO_SCAN 1000
31
32
33struct rsn_pmksa_candidate {
34	struct dl_list list;
35	u8 bssid[ETH_ALEN];
36	int priority;
37};
38
39
40/**
41 * pmksa_candidate_free - Free all entries in PMKSA candidate list
42 * @sm: Pointer to WPA state machine data from wpa_sm_init()
43 */
44void pmksa_candidate_free(struct wpa_sm *sm)
45{
46	struct rsn_pmksa_candidate *entry, *n;
47
48	if (sm == NULL)
49		return;
50
51	dl_list_for_each_safe(entry, n, &sm->pmksa_candidates,
52			      struct rsn_pmksa_candidate, list) {
53		dl_list_del(&entry->list);
54		os_free(entry);
55	}
56}
57
58
59static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
60				const u8 *buf, size_t len)
61{
62	struct wpa_sm *sm = ctx;
63
64	wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr));
65	wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len);
66
67	if (sm->preauth_eapol == NULL ||
68	    is_zero_ether_addr(sm->preauth_bssid) ||
69	    os_memcmp(sm->preauth_bssid, src_addr, ETH_ALEN) != 0) {
70		wpa_printf(MSG_WARNING, "RSN pre-auth frame received from "
71			   "unexpected source " MACSTR " - dropped",
72			   MAC2STR(src_addr));
73		return;
74	}
75
76	eapol_sm_rx_eapol(sm->preauth_eapol, src_addr, buf, len);
77}
78
79
80static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success,
81				 void *ctx)
82{
83	struct wpa_sm *sm = ctx;
84	u8 pmk[PMK_LEN];
85
86	if (success) {
87		int res, pmk_len;
88		pmk_len = PMK_LEN;
89		res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
90		if (res) {
91			/*
92			 * EAP-LEAP is an exception from other EAP methods: it
93			 * uses only 16-byte PMK.
94			 */
95			res = eapol_sm_get_key(eapol, pmk, 16);
96			pmk_len = 16;
97		}
98		if (res == 0) {
99			wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth",
100					pmk, pmk_len);
101			sm->pmk_len = pmk_len;
102			pmksa_cache_add(sm->pmksa, pmk, pmk_len,
103					sm->preauth_bssid, sm->own_addr,
104					sm->network_ctx,
105					WPA_KEY_MGMT_IEEE8021X);
106		} else {
107			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
108				"RSN: failed to get master session key from "
109				"pre-auth EAPOL state machines");
110			success = 0;
111		}
112	}
113
114	wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with "
115		MACSTR " %s", MAC2STR(sm->preauth_bssid),
116		success ? "completed successfully" : "failed");
117
118	rsn_preauth_deinit(sm);
119	rsn_preauth_candidate_process(sm);
120}
121
122
123static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx)
124{
125	struct wpa_sm *sm = eloop_ctx;
126
127	wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with "
128		MACSTR " timed out", MAC2STR(sm->preauth_bssid));
129	rsn_preauth_deinit(sm);
130	rsn_preauth_candidate_process(sm);
131}
132
133
134static int rsn_preauth_eapol_send(void *ctx, int type, const u8 *buf,
135				  size_t len)
136{
137	struct wpa_sm *sm = ctx;
138	u8 *msg;
139	size_t msglen;
140	int res;
141
142	/* TODO: could add l2_packet_sendmsg that allows fragments to avoid
143	 * extra copy here */
144
145	if (sm->l2_preauth == NULL)
146		return -1;
147
148	msg = wpa_sm_alloc_eapol(sm, type, buf, len, &msglen, NULL);
149	if (msg == NULL)
150		return -1;
151
152	wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen);
153	res = l2_packet_send(sm->l2_preauth, sm->preauth_bssid,
154			     ETH_P_RSN_PREAUTH, msg, msglen);
155	os_free(msg);
156	return res;
157}
158
159
160/**
161 * rsn_preauth_init - Start new RSN pre-authentication
162 * @sm: Pointer to WPA state machine data from wpa_sm_init()
163 * @dst: Authenticator address (BSSID) with which to preauthenticate
164 * @eap_conf: Current EAP configuration
165 * Returns: 0 on success, -1 on another pre-authentication is in progress,
166 * -2 on layer 2 packet initialization failure, -3 on EAPOL state machine
167 * initialization failure, -4 on memory allocation failure
168 *
169 * This function request an RSN pre-authentication with a given destination
170 * address. This is usually called for PMKSA candidates found from scan results
171 * or from driver reports. In addition, ctrl_iface PREAUTH command can trigger
172 * pre-authentication.
173 */
174int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
175		     struct eap_peer_config *eap_conf)
176{
177	struct eapol_config eapol_conf;
178	struct eapol_ctx *ctx;
179
180	if (sm->preauth_eapol)
181		return -1;
182
183	wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG,
184		"RSN: starting pre-authentication with " MACSTR, MAC2STR(dst));
185
186	sm->l2_preauth = l2_packet_init(sm->ifname, sm->own_addr,
187					ETH_P_RSN_PREAUTH,
188					rsn_preauth_receive, sm, 0);
189	if (sm->l2_preauth == NULL) {
190		wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet "
191			   "processing for pre-authentication");
192		return -2;
193	}
194
195	if (sm->bridge_ifname) {
196		sm->l2_preauth_br = l2_packet_init(sm->bridge_ifname,
197						   sm->own_addr,
198						   ETH_P_RSN_PREAUTH,
199						   rsn_preauth_receive, sm, 0);
200		if (sm->l2_preauth_br == NULL) {
201			wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 "
202				   "packet processing (bridge) for "
203				   "pre-authentication");
204			return -2;
205		}
206	}
207
208	ctx = os_zalloc(sizeof(*ctx));
209	if (ctx == NULL) {
210		wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context.");
211		return -4;
212	}
213	ctx->ctx = sm->ctx->ctx;
214	ctx->msg_ctx = sm->ctx->ctx;
215	ctx->preauth = 1;
216	ctx->cb = rsn_preauth_eapol_cb;
217	ctx->cb_ctx = sm;
218	ctx->scard_ctx = sm->scard_ctx;
219	ctx->eapol_send = rsn_preauth_eapol_send;
220	ctx->eapol_send_ctx = sm;
221	ctx->set_config_blob = sm->ctx->set_config_blob;
222	ctx->get_config_blob = sm->ctx->get_config_blob;
223
224	sm->preauth_eapol = eapol_sm_init(ctx);
225	if (sm->preauth_eapol == NULL) {
226		os_free(ctx);
227		wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL "
228			   "state machines for pre-authentication");
229		return -3;
230	}
231	os_memset(&eapol_conf, 0, sizeof(eapol_conf));
232	eapol_conf.accept_802_1x_keys = 0;
233	eapol_conf.required_keys = 0;
234	eapol_conf.fast_reauth = sm->fast_reauth;
235	eapol_conf.workaround = sm->eap_workaround;
236	eapol_sm_notify_config(sm->preauth_eapol, eap_conf, &eapol_conf);
237	/*
238	 * Use a shorter startPeriod with preauthentication since the first
239	 * preauth EAPOL-Start frame may end up being dropped due to race
240	 * condition in the AP between the data receive and key configuration
241	 * after the 4-Way Handshake.
242	 */
243	eapol_sm_configure(sm->preauth_eapol, -1, -1, 5, 6);
244	os_memcpy(sm->preauth_bssid, dst, ETH_ALEN);
245
246	eapol_sm_notify_portValid(sm->preauth_eapol, TRUE);
247	/* 802.1X::portControl = Auto */
248	eapol_sm_notify_portEnabled(sm->preauth_eapol, TRUE);
249
250	eloop_register_timeout(sm->dot11RSNAConfigSATimeout, 0,
251			       rsn_preauth_timeout, sm, NULL);
252
253	return 0;
254}
255
256
257/**
258 * rsn_preauth_deinit - Abort RSN pre-authentication
259 * @sm: Pointer to WPA state machine data from wpa_sm_init()
260 *
261 * This function aborts the current RSN pre-authentication (if one is started)
262 * and frees resources allocated for it.
263 */
264void rsn_preauth_deinit(struct wpa_sm *sm)
265{
266	if (sm == NULL || !sm->preauth_eapol)
267		return;
268
269	eloop_cancel_timeout(rsn_preauth_timeout, sm, NULL);
270	eapol_sm_deinit(sm->preauth_eapol);
271	sm->preauth_eapol = NULL;
272	os_memset(sm->preauth_bssid, 0, ETH_ALEN);
273
274	l2_packet_deinit(sm->l2_preauth);
275	sm->l2_preauth = NULL;
276	if (sm->l2_preauth_br) {
277		l2_packet_deinit(sm->l2_preauth_br);
278		sm->l2_preauth_br = NULL;
279	}
280}
281
282
283/**
284 * rsn_preauth_candidate_process - Process PMKSA candidates
285 * @sm: Pointer to WPA state machine data from wpa_sm_init()
286 *
287 * Go through the PMKSA candidates and start pre-authentication if a candidate
288 * without an existing PMKSA cache entry is found. Processed candidates will be
289 * removed from the list.
290 */
291void rsn_preauth_candidate_process(struct wpa_sm *sm)
292{
293	struct rsn_pmksa_candidate *candidate, *n;
294
295	if (dl_list_empty(&sm->pmksa_candidates))
296		return;
297
298	/* TODO: drop priority for old candidate entries */
299
300	wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: processing PMKSA candidate "
301		"list");
302	if (sm->preauth_eapol ||
303	    sm->proto != WPA_PROTO_RSN ||
304	    wpa_sm_get_state(sm) != WPA_COMPLETED ||
305	    (sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X &&
306	     sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256)) {
307		wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: not in suitable "
308			"state for new pre-authentication");
309		return; /* invalid state for new pre-auth */
310	}
311
312	dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates,
313			      struct rsn_pmksa_candidate, list) {
314		struct rsn_pmksa_cache_entry *p = NULL;
315		p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL);
316		if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 &&
317		    (p == NULL || p->opportunistic)) {
318			wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA "
319				"candidate " MACSTR
320				" selected for pre-authentication",
321				MAC2STR(candidate->bssid));
322			dl_list_del(&candidate->list);
323			rsn_preauth_init(sm, candidate->bssid,
324					 sm->eap_conf_ctx);
325			os_free(candidate);
326			return;
327		}
328		wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA candidate "
329			MACSTR " does not need pre-authentication anymore",
330			MAC2STR(candidate->bssid));
331		/* Some drivers (e.g., NDIS) expect to get notified about the
332		 * PMKIDs again, so report the existing data now. */
333		if (p) {
334			wpa_sm_add_pmkid(sm, candidate->bssid, p->pmkid);
335		}
336
337		dl_list_del(&candidate->list);
338		os_free(candidate);
339	}
340	wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no more pending PMKSA "
341		"candidates");
342}
343
344
345/**
346 * pmksa_candidate_add - Add a new PMKSA candidate
347 * @sm: Pointer to WPA state machine data from wpa_sm_init()
348 * @bssid: BSSID (authenticator address) of the candidate
349 * @prio: Priority (the smaller number, the higher priority)
350 * @preauth: Whether the candidate AP advertises support for pre-authentication
351 *
352 * This function is used to add PMKSA candidates for RSN pre-authentication. It
353 * is called from scan result processing and from driver events for PMKSA
354 * candidates, i.e., EVENT_PMKID_CANDIDATE events to wpa_supplicant_event().
355 */
356void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid,
357			 int prio, int preauth)
358{
359	struct rsn_pmksa_candidate *cand, *pos;
360
361	if (sm->network_ctx && sm->proactive_key_caching)
362		pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx,
363					      bssid);
364
365	if (!preauth) {
366		wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without "
367			   "preauth flag");
368		return;
369	}
370
371	/* If BSSID already on candidate list, update the priority of the old
372	 * entry. Do not override priority based on normal scan results. */
373	cand = NULL;
374	dl_list_for_each(pos, &sm->pmksa_candidates,
375			 struct rsn_pmksa_candidate, list) {
376		if (os_memcmp(pos->bssid, bssid, ETH_ALEN) == 0) {
377			cand = pos;
378			break;
379		}
380	}
381
382	if (cand) {
383		dl_list_del(&cand->list);
384		if (prio < PMKID_CANDIDATE_PRIO_SCAN)
385			cand->priority = prio;
386	} else {
387		cand = os_zalloc(sizeof(*cand));
388		if (cand == NULL)
389			return;
390		os_memcpy(cand->bssid, bssid, ETH_ALEN);
391		cand->priority = prio;
392	}
393
394	/* Add candidate to the list; order by increasing priority value. i.e.,
395	 * highest priority (smallest value) first. */
396	dl_list_for_each(pos, &sm->pmksa_candidates,
397			 struct rsn_pmksa_candidate, list) {
398		if (cand->priority <= pos->priority) {
399			dl_list_add(pos->list.prev, &cand->list);
400			cand = NULL;
401			break;
402		}
403	}
404	if (cand)
405		dl_list_add_tail(&sm->pmksa_candidates, &cand->list);
406
407	wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: added PMKSA cache "
408		"candidate " MACSTR " prio %d", MAC2STR(bssid), prio);
409	rsn_preauth_candidate_process(sm);
410}
411
412
413/* TODO: schedule periodic scans if current AP supports preauth */
414
415/**
416 * rsn_preauth_scan_results - Start processing scan results for canditates
417 * @sm: Pointer to WPA state machine data from wpa_sm_init()
418 * Returns: 0 if ready to process results or -1 to skip processing
419 *
420 * This functions is used to notify RSN code about start of new scan results
421 * processing. The actual scan results will be provided by calling
422 * rsn_preauth_scan_result() for each BSS if this function returned 0.
423 */
424int rsn_preauth_scan_results(struct wpa_sm *sm)
425{
426	if (sm->ssid_len == 0)
427		return -1;
428
429	/*
430	 * TODO: is it ok to free all candidates? What about the entries
431	 * received from EVENT_PMKID_CANDIDATE?
432	 */
433	pmksa_candidate_free(sm);
434
435	return 0;
436}
437
438
439/**
440 * rsn_preauth_scan_result - Processing scan result for PMKSA canditates
441 * @sm: Pointer to WPA state machine data from wpa_sm_init()
442 *
443 * Add all suitable APs (Authenticators) from scan results into PMKSA
444 * candidate list.
445 */
446void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid,
447			     const u8 *ssid, const u8 *rsn)
448{
449	struct wpa_ie_data ie;
450	struct rsn_pmksa_cache_entry *pmksa;
451
452	if (ssid[1] != sm->ssid_len ||
453	    os_memcmp(ssid + 2, sm->ssid, sm->ssid_len) != 0)
454		return; /* Not for the current SSID */
455
456	if (os_memcmp(bssid, sm->bssid, ETH_ALEN) == 0)
457		return; /* Ignore current AP */
458
459	if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie))
460		return;
461
462	pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL);
463	if (pmksa && (!pmksa->opportunistic ||
464		      !(ie.capabilities & WPA_CAPABILITY_PREAUTH)))
465		return;
466
467	/* Give less priority to candidates found from normal scan results. */
468	pmksa_candidate_add(sm, bssid, PMKID_CANDIDATE_PRIO_SCAN,
469			    ie.capabilities & WPA_CAPABILITY_PREAUTH);
470}
471
472
473#ifdef CONFIG_CTRL_IFACE
474/**
475 * rsn_preauth_get_status - Get pre-authentication status
476 * @sm: Pointer to WPA state machine data from wpa_sm_init()
477 * @buf: Buffer for status information
478 * @buflen: Maximum buffer length
479 * @verbose: Whether to include verbose status information
480 * Returns: Number of bytes written to buf.
481 *
482 * Query WPA2 pre-authentication for status information. This function fills in
483 * a text area with current status information. If the buffer (buf) is not
484 * large enough, status information will be truncated to fit the buffer.
485 */
486int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
487			   int verbose)
488{
489	char *pos = buf, *end = buf + buflen;
490	int res, ret;
491
492	if (sm->preauth_eapol) {
493		ret = os_snprintf(pos, end - pos, "Pre-authentication "
494				  "EAPOL state machines:\n");
495		if (ret < 0 || ret >= end - pos)
496			return pos - buf;
497		pos += ret;
498		res = eapol_sm_get_status(sm->preauth_eapol,
499					  pos, end - pos, verbose);
500		if (res >= 0)
501			pos += res;
502	}
503
504	return pos - buf;
505}
506#endif /* CONFIG_CTRL_IFACE */
507
508
509/**
510 * rsn_preauth_in_progress - Verify whether pre-authentication is in progress
511 * @sm: Pointer to WPA state machine data from wpa_sm_init()
512 */
513int rsn_preauth_in_progress(struct wpa_sm *sm)
514{
515	return sm->preauth_eapol != NULL;
516}
517
518#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
519