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