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