]>
Commit | Line | Data |
---|---|---|
c2a04078 JM |
1 | /* |
2 | * wpa_supplicant - SME | |
3 | * Copyright (c) 2009, 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" | |
c2a04078 JM |
18 | #include "ieee802_11_defs.h" |
19 | #include "eapol_supp/eapol_supp_sm.h" | |
20 | #include "wpa_common.h" | |
21 | #include "wpa.h" | |
22 | #include "pmksa_cache.h" | |
23 | #include "config.h" | |
24 | #include "wpa_supplicant_i.h" | |
2d5b792d | 25 | #include "driver_i.h" |
c2a04078 JM |
26 | #include "wpas_glue.h" |
27 | #include "wps_supplicant.h" | |
8bac466b | 28 | #include "notify.h" |
76d11d3f | 29 | #include "blacklist.h" |
c2a04078 JM |
30 | #include "sme.h" |
31 | ||
32 | void sme_authenticate(struct wpa_supplicant *wpa_s, | |
33 | struct wpa_scan_res *bss, struct wpa_ssid *ssid) | |
34 | { | |
35 | struct wpa_driver_auth_params params; | |
8bac466b | 36 | struct wpa_ssid *old_ssid; |
c2a04078 JM |
37 | const u8 *ie; |
38 | #ifdef CONFIG_IEEE80211R | |
39 | const u8 *md = NULL; | |
40 | #endif /* CONFIG_IEEE80211R */ | |
8bac466b | 41 | int i, bssid_changed; |
c2a04078 JM |
42 | |
43 | if (bss == NULL) { | |
44 | wpa_printf(MSG_ERROR, "SME: No scan result available for the " | |
45 | "network"); | |
46 | return; | |
47 | } | |
48 | ||
49 | os_memset(¶ms, 0, sizeof(params)); | |
50 | wpa_s->reassociate = 0; | |
51 | ||
52 | params.freq = bss->freq; | |
53 | params.bssid = bss->bssid; | |
54 | ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); | |
55 | if (ie == NULL) { | |
56 | wpa_printf(MSG_ERROR, "SME: SSID not available for the BSS"); | |
57 | return; | |
58 | } | |
59 | params.ssid = ie + 2; | |
60 | params.ssid_len = ie[1]; | |
61 | ||
62 | wpa_s->sme.freq = params.freq; | |
63 | os_memcpy(wpa_s->sme.ssid, params.ssid, params.ssid_len); | |
64 | wpa_s->sme.ssid_len = params.ssid_len; | |
65 | ||
66 | params.auth_alg = AUTH_ALG_OPEN_SYSTEM; | |
67 | #ifdef IEEE8021X_EAPOL | |
68 | if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { | |
69 | if (ssid->leap) { | |
70 | if (ssid->non_leap == 0) | |
71 | params.auth_alg = AUTH_ALG_LEAP; | |
72 | else | |
73 | params.auth_alg |= AUTH_ALG_LEAP; | |
74 | } | |
75 | } | |
76 | #endif /* IEEE8021X_EAPOL */ | |
77 | wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x", | |
78 | params.auth_alg); | |
79 | if (ssid->auth_alg) { | |
80 | params.auth_alg = 0; | |
81 | if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) | |
82 | params.auth_alg |= AUTH_ALG_OPEN_SYSTEM; | |
83 | if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) | |
84 | params.auth_alg |= AUTH_ALG_SHARED_KEY; | |
85 | if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) | |
86 | params.auth_alg |= AUTH_ALG_LEAP; | |
87 | wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x", | |
88 | params.auth_alg); | |
89 | } | |
90 | ||
a0b2f99b JM |
91 | for (i = 0; i < NUM_WEP_KEYS; i++) { |
92 | if (ssid->wep_key_len[i]) | |
93 | params.wep_key[i] = ssid->wep_key[i]; | |
94 | params.wep_key_len[i] = ssid->wep_key_len[i]; | |
95 | } | |
96 | params.wep_tx_keyidx = ssid->wep_tx_keyidx; | |
97 | ||
8bac466b | 98 | bssid_changed = !is_zero_ether_addr(wpa_s->bssid); |
c2a04078 JM |
99 | os_memset(wpa_s->bssid, 0, ETH_ALEN); |
100 | os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); | |
8bac466b JM |
101 | if (bssid_changed) |
102 | wpas_notify_bssid_changed(wpa_s); | |
c2a04078 JM |
103 | |
104 | if (bss && (wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || | |
105 | wpa_scan_get_ie(bss, WLAN_EID_RSN)) && | |
106 | (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK | | |
107 | WPA_KEY_MGMT_FT_IEEE8021X | | |
108 | WPA_KEY_MGMT_FT_PSK | | |
109 | WPA_KEY_MGMT_IEEE8021X_SHA256 | | |
110 | WPA_KEY_MGMT_PSK_SHA256))) { | |
111 | int try_opportunistic; | |
112 | try_opportunistic = ssid->proactive_key_caching && | |
113 | (ssid->proto & WPA_PROTO_RSN); | |
114 | if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, | |
115 | wpa_s->current_ssid, | |
116 | try_opportunistic) == 0) | |
117 | eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); | |
118 | wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); | |
119 | if (wpa_supplicant_set_suites(wpa_s, bss, ssid, | |
120 | wpa_s->sme.assoc_req_ie, | |
121 | &wpa_s->sme.assoc_req_ie_len)) { | |
122 | wpa_printf(MSG_WARNING, "SME: Failed to set WPA key " | |
123 | "management and encryption suites"); | |
124 | return; | |
125 | } | |
126 | } else if (ssid->key_mgmt & | |
127 | (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | | |
128 | WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK | | |
129 | WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 | | |
130 | WPA_KEY_MGMT_IEEE8021X_SHA256)) { | |
131 | wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); | |
132 | if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, | |
133 | wpa_s->sme.assoc_req_ie, | |
134 | &wpa_s->sme.assoc_req_ie_len)) { | |
135 | wpa_printf(MSG_WARNING, "SME: Failed to set WPA key " | |
136 | "management and encryption suites (no scan " | |
137 | "results)"); | |
138 | return; | |
139 | } | |
140 | #ifdef CONFIG_WPS | |
141 | } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { | |
142 | struct wpabuf *wps_ie; | |
143 | wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid)); | |
144 | if (wps_ie && wpabuf_len(wps_ie) <= | |
145 | sizeof(wpa_s->sme.assoc_req_ie)) { | |
146 | wpa_s->sme.assoc_req_ie_len = wpabuf_len(wps_ie); | |
147 | os_memcpy(wpa_s->sme.assoc_req_ie, wpabuf_head(wps_ie), | |
148 | wpa_s->sme.assoc_req_ie_len); | |
149 | } else | |
150 | wpa_s->sme.assoc_req_ie_len = 0; | |
151 | wpabuf_free(wps_ie); | |
152 | wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); | |
153 | #endif /* CONFIG_WPS */ | |
154 | } else { | |
155 | wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); | |
156 | wpa_s->sme.assoc_req_ie_len = 0; | |
157 | } | |
158 | ||
159 | #ifdef CONFIG_IEEE80211R | |
160 | ie = wpa_scan_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); | |
161 | if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN) | |
162 | md = ie + 2; | |
163 | wpa_sm_set_ft_params(wpa_s->wpa, md, NULL, 0, NULL); | |
164 | if (md) { | |
165 | /* Prepare for the next transition */ | |
166 | wpa_ft_prepare_auth_request(wpa_s->wpa); | |
167 | } | |
168 | ||
169 | if (md && ssid->key_mgmt & (WPA_KEY_MGMT_FT_PSK | | |
170 | WPA_KEY_MGMT_FT_IEEE8021X)) { | |
171 | if (wpa_s->sme.assoc_req_ie_len + 5 < | |
172 | sizeof(wpa_s->sme.assoc_req_ie)) { | |
173 | struct rsn_mdie *mdie; | |
174 | u8 *pos = wpa_s->sme.assoc_req_ie + | |
175 | wpa_s->sme.assoc_req_ie_len; | |
176 | *pos++ = WLAN_EID_MOBILITY_DOMAIN; | |
177 | *pos++ = sizeof(*mdie); | |
178 | mdie = (struct rsn_mdie *) pos; | |
179 | os_memcpy(mdie->mobility_domain, md, | |
180 | MOBILITY_DOMAIN_ID_LEN); | |
181 | mdie->ft_capab = 0; | |
182 | wpa_s->sme.assoc_req_ie_len += 5; | |
183 | } | |
184 | ||
185 | if (wpa_s->sme.ft_used && | |
186 | os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0) { | |
187 | wpa_printf(MSG_DEBUG, "SME: Trying to use FT " | |
188 | "over-the-air"); | |
189 | params.auth_alg = AUTH_ALG_FT; | |
190 | params.ie = wpa_s->sme.ft_ies; | |
191 | params.ie_len = wpa_s->sme.ft_ies_len; | |
192 | } | |
193 | } | |
194 | #endif /* CONFIG_IEEE80211R */ | |
195 | ||
196 | #ifdef CONFIG_IEEE80211W | |
197 | switch (ssid->ieee80211w) { | |
198 | case NO_IEEE80211W: | |
199 | wpa_s->sme.mfp = NO_MGMT_FRAME_PROTECTION; | |
200 | break; | |
201 | case IEEE80211W_OPTIONAL: | |
202 | wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_OPTIONAL; | |
203 | break; | |
204 | case IEEE80211W_REQUIRED: | |
205 | wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED; | |
206 | break; | |
207 | } | |
208 | if (ssid->ieee80211w != NO_IEEE80211W && bss) { | |
209 | const u8 *rsn = wpa_scan_get_ie(bss, WLAN_EID_RSN); | |
210 | struct wpa_ie_data _ie; | |
211 | if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 && | |
212 | _ie.capabilities & | |
213 | (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) { | |
214 | wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: " | |
215 | "require MFP"); | |
216 | wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED; | |
217 | } | |
218 | } | |
219 | #endif /* CONFIG_IEEE80211W */ | |
220 | ||
221 | wpa_supplicant_cancel_scan(wpa_s); | |
222 | ||
223 | wpa_msg(wpa_s, MSG_INFO, "Trying to authenticate with " MACSTR | |
224 | " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid), | |
225 | wpa_ssid_txt(params.ssid, params.ssid_len), params.freq); | |
226 | ||
227 | wpa_clear_keys(wpa_s, bss->bssid); | |
228 | wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); | |
8bac466b | 229 | old_ssid = wpa_s->current_ssid; |
c2a04078 JM |
230 | wpa_s->current_ssid = ssid; |
231 | wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); | |
232 | wpa_supplicant_initiate_eapol(wpa_s); | |
8bac466b JM |
233 | if (old_ssid != wpa_s->current_ssid) |
234 | wpas_notify_network_changed(wpa_s); | |
c2a04078 JM |
235 | |
236 | if (wpa_drv_authenticate(wpa_s, ¶ms) < 0) { | |
237 | wpa_msg(wpa_s, MSG_INFO, "Authentication request to the " | |
238 | "driver failed"); | |
239 | return; | |
240 | } | |
241 | ||
242 | /* TODO: add timeout on authentication */ | |
243 | ||
244 | /* | |
245 | * Association will be started based on the authentication event from | |
246 | * the driver. | |
247 | */ | |
248 | } | |
249 | ||
250 | ||
251 | void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) | |
252 | { | |
253 | struct wpa_driver_associate_params params; | |
254 | struct wpa_ssid *ssid = wpa_s->current_ssid; | |
255 | ||
256 | if (ssid == NULL) { | |
257 | wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when " | |
258 | "network is not selected"); | |
259 | return; | |
260 | } | |
261 | ||
262 | if (wpa_s->wpa_state != WPA_AUTHENTICATING) { | |
263 | wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when " | |
264 | "not in authenticating state"); | |
265 | return; | |
266 | } | |
267 | ||
268 | if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) { | |
269 | wpa_printf(MSG_DEBUG, "SME: Ignore authentication with " | |
270 | "unexpected peer " MACSTR, | |
271 | MAC2STR(data->auth.peer)); | |
272 | return; | |
273 | } | |
274 | ||
275 | wpa_printf(MSG_DEBUG, "SME: Authentication response: peer=" MACSTR | |
276 | " auth_type=%d status_code=%d", | |
277 | MAC2STR(data->auth.peer), data->auth.auth_type, | |
278 | data->auth.status_code); | |
279 | wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs", | |
280 | data->auth.ies, data->auth.ies_len); | |
281 | ||
282 | if (data->auth.status_code != WLAN_STATUS_SUCCESS) { | |
283 | wpa_printf(MSG_DEBUG, "SME: Authentication failed (status " | |
284 | "code %d)", data->auth.status_code); | |
285 | return; | |
286 | } | |
287 | ||
288 | #ifdef CONFIG_IEEE80211R | |
289 | if (data->auth.auth_type == WLAN_AUTH_FT) { | |
290 | union wpa_event_data edata; | |
291 | os_memset(&edata, 0, sizeof(edata)); | |
292 | edata.ft_ies.ies = data->auth.ies; | |
293 | edata.ft_ies.ies_len = data->auth.ies_len; | |
294 | os_memcpy(edata.ft_ies.target_ap, data->auth.peer, ETH_ALEN); | |
295 | wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &edata); | |
296 | } | |
297 | #endif /* CONFIG_IEEE80211R */ | |
298 | ||
299 | os_memset(¶ms, 0, sizeof(params)); | |
300 | params.bssid = data->auth.peer; | |
301 | params.ssid = wpa_s->sme.ssid; | |
302 | params.ssid_len = wpa_s->sme.ssid_len; | |
303 | params.freq = wpa_s->sme.freq; | |
304 | params.wpa_ie = wpa_s->sme.assoc_req_ie_len ? | |
305 | wpa_s->sme.assoc_req_ie : NULL; | |
306 | params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len; | |
307 | #ifdef CONFIG_IEEE80211R | |
308 | if (data->auth.auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) { | |
309 | params.wpa_ie = wpa_s->sme.ft_ies; | |
310 | params.wpa_ie_len = wpa_s->sme.ft_ies_len; | |
311 | } | |
312 | #endif /* CONFIG_IEEE80211R */ | |
313 | params.mode = ssid->mode; | |
314 | params.mgmt_frame_protection = wpa_s->sme.mfp; | |
315 | ||
316 | wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR | |
317 | " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid), | |
318 | params.ssid ? wpa_ssid_txt(params.ssid, params.ssid_len) : "", | |
319 | params.freq); | |
320 | ||
321 | wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING); | |
322 | ||
323 | if (wpa_drv_associate(wpa_s, ¶ms) < 0) { | |
324 | wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " | |
325 | "failed"); | |
326 | return; | |
327 | } | |
328 | ||
329 | /* TODO: add timeout on association */ | |
330 | } | |
331 | ||
332 | ||
333 | int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, | |
334 | const u8 *ies, size_t ies_len) | |
335 | { | |
336 | if (md == NULL || ies == NULL) { | |
337 | wpa_printf(MSG_DEBUG, "SME: Remove mobility domain"); | |
338 | os_free(wpa_s->sme.ft_ies); | |
339 | wpa_s->sme.ft_ies = NULL; | |
340 | wpa_s->sme.ft_ies_len = 0; | |
341 | wpa_s->sme.ft_used = 0; | |
342 | return 0; | |
343 | } | |
344 | ||
345 | os_memcpy(wpa_s->sme.mobility_domain, md, MOBILITY_DOMAIN_ID_LEN); | |
346 | wpa_hexdump(MSG_DEBUG, "SME: FT IEs", ies, ies_len); | |
347 | os_free(wpa_s->sme.ft_ies); | |
348 | wpa_s->sme.ft_ies = os_malloc(ies_len); | |
349 | if (wpa_s->sme.ft_ies == NULL) | |
350 | return -1; | |
351 | os_memcpy(wpa_s->sme.ft_ies, ies, ies_len); | |
352 | wpa_s->sme.ft_ies_len = ies_len; | |
353 | return 0; | |
354 | } | |
efa46078 JM |
355 | |
356 | ||
357 | void sme_event_assoc_reject(struct wpa_supplicant *wpa_s, | |
358 | union wpa_event_data *data) | |
359 | { | |
8bac466b | 360 | int bssid_changed; |
76d11d3f | 361 | int timeout = 5000; |
8bac466b | 362 | |
76d11d3f JM |
363 | wpa_printf(MSG_DEBUG, "SME: Association with " MACSTR " failed: " |
364 | "status code %d", MAC2STR(wpa_s->pending_bssid), | |
efa46078 JM |
365 | data->assoc_reject.status_code); |
366 | ||
8bac466b | 367 | bssid_changed = !is_zero_ether_addr(wpa_s->bssid); |
76d11d3f JM |
368 | |
369 | /* | |
370 | * For now, unconditionally terminate the previous authentication. In | |
371 | * theory, this should not be needed, but mac80211 gets quite confused | |
372 | * if the authentication is left pending.. Some roaming cases might | |
373 | * benefit from using the previous authentication, so this could be | |
374 | * optimized in the future. | |
375 | */ | |
376 | if (wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid, | |
377 | WLAN_REASON_DEAUTH_LEAVING) < 0) { | |
378 | wpa_msg(wpa_s, MSG_INFO, | |
379 | "Deauth request to the driver failed"); | |
380 | } | |
381 | ||
382 | if (wpa_blacklist_add(wpa_s, wpa_s->pending_bssid) == 0) { | |
383 | struct wpa_blacklist *b; | |
384 | b = wpa_blacklist_get(wpa_s, wpa_s->pending_bssid); | |
385 | if (b && b->count < 3) { | |
386 | /* | |
387 | * Speed up next attempt if there could be other APs | |
388 | * that could accept association. | |
389 | */ | |
390 | timeout = 100; | |
391 | } | |
392 | } | |
393 | wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); | |
efa46078 JM |
394 | os_memset(wpa_s->bssid, 0, ETH_ALEN); |
395 | os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); | |
8bac466b JM |
396 | if (bssid_changed) |
397 | wpas_notify_bssid_changed(wpa_s); | |
efa46078 JM |
398 | |
399 | /* | |
400 | * TODO: if more than one possible AP is available in scan results, | |
401 | * could try the other ones before requesting a new scan. | |
402 | */ | |
76d11d3f JM |
403 | wpa_supplicant_req_scan(wpa_s, timeout / 1000, |
404 | 1000 * (timeout % 1000)); | |
efa46078 | 405 | } |
da1fb17c JM |
406 | |
407 | ||
408 | void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s, | |
409 | union wpa_event_data *data) | |
410 | { | |
411 | wpa_printf(MSG_DEBUG, "SME: Authentication timed out"); | |
412 | wpa_supplicant_req_scan(wpa_s, 5, 0); | |
413 | } | |
414 | ||
415 | ||
416 | void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, | |
417 | union wpa_event_data *data) | |
418 | { | |
419 | wpa_printf(MSG_DEBUG, "SME: Association timed out"); | |
420 | wpa_supplicant_mark_disassoc(wpa_s); | |
421 | wpa_supplicant_req_scan(wpa_s, 5, 0); | |
422 | } |