]>
Commit | Line | Data |
---|---|---|
c442055e JM |
1 | /* |
2 | * hostapd / WPA authenticator glue code | |
3 | * Copyright (c) 2002-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 "utils/includes.h" | |
16 | ||
17 | #include "utils/common.h" | |
18 | #include "common/ieee802_11_defs.h" | |
19 | #include "eapol_auth/eapol_auth_sm.h" | |
20 | #include "eapol_auth/eapol_auth_sm_i.h" | |
21 | #include "eap_server/eap.h" | |
22 | #include "l2_packet/l2_packet.h" | |
6226e38d JM |
23 | #include "hostapd.h" |
24 | #include "ieee802_1x.h" | |
25 | #include "preauth_auth.h" | |
26 | #include "sta_info.h" | |
27 | #include "tkip_countermeasures.h" | |
28 | #include "wpa_auth.h" | |
c442055e JM |
29 | #include "driver_i.h" |
30 | ||
31 | ||
32 | static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, | |
33 | struct wpa_auth_config *wconf) | |
34 | { | |
35 | wconf->wpa = conf->wpa; | |
36 | wconf->wpa_key_mgmt = conf->wpa_key_mgmt; | |
37 | wconf->wpa_pairwise = conf->wpa_pairwise; | |
38 | wconf->wpa_group = conf->wpa_group; | |
39 | wconf->wpa_group_rekey = conf->wpa_group_rekey; | |
40 | wconf->wpa_strict_rekey = conf->wpa_strict_rekey; | |
41 | wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey; | |
42 | wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey; | |
43 | wconf->rsn_pairwise = conf->rsn_pairwise; | |
44 | wconf->rsn_preauth = conf->rsn_preauth; | |
45 | wconf->eapol_version = conf->eapol_version; | |
46 | wconf->peerkey = conf->peerkey; | |
47 | wconf->wmm_enabled = conf->wmm_enabled; | |
48 | wconf->okc = conf->okc; | |
49 | #ifdef CONFIG_IEEE80211W | |
50 | wconf->ieee80211w = conf->ieee80211w; | |
51 | #endif /* CONFIG_IEEE80211W */ | |
52 | #ifdef CONFIG_IEEE80211R | |
53 | wconf->ssid_len = conf->ssid.ssid_len; | |
54 | if (wconf->ssid_len > SSID_LEN) | |
55 | wconf->ssid_len = SSID_LEN; | |
56 | os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len); | |
57 | os_memcpy(wconf->mobility_domain, conf->mobility_domain, | |
58 | MOBILITY_DOMAIN_ID_LEN); | |
59 | if (conf->nas_identifier && | |
60 | os_strlen(conf->nas_identifier) <= FT_R0KH_ID_MAX_LEN) { | |
61 | wconf->r0_key_holder_len = os_strlen(conf->nas_identifier); | |
62 | os_memcpy(wconf->r0_key_holder, conf->nas_identifier, | |
63 | wconf->r0_key_holder_len); | |
64 | } | |
65 | os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN); | |
66 | wconf->r0_key_lifetime = conf->r0_key_lifetime; | |
67 | wconf->reassociation_deadline = conf->reassociation_deadline; | |
68 | wconf->r0kh_list = conf->r0kh_list; | |
69 | wconf->r1kh_list = conf->r1kh_list; | |
70 | wconf->pmk_r1_push = conf->pmk_r1_push; | |
71 | #endif /* CONFIG_IEEE80211R */ | |
72 | } | |
73 | ||
74 | ||
75 | static void hostapd_wpa_auth_logger(void *ctx, const u8 *addr, | |
76 | logger_level level, const char *txt) | |
77 | { | |
78 | #ifndef CONFIG_NO_HOSTAPD_LOGGER | |
79 | struct hostapd_data *hapd = ctx; | |
80 | int hlevel; | |
81 | ||
82 | switch (level) { | |
83 | case LOGGER_WARNING: | |
84 | hlevel = HOSTAPD_LEVEL_WARNING; | |
85 | break; | |
86 | case LOGGER_INFO: | |
87 | hlevel = HOSTAPD_LEVEL_INFO; | |
88 | break; | |
89 | case LOGGER_DEBUG: | |
90 | default: | |
91 | hlevel = HOSTAPD_LEVEL_DEBUG; | |
92 | break; | |
93 | } | |
94 | ||
95 | hostapd_logger(hapd, addr, HOSTAPD_MODULE_WPA, hlevel, "%s", txt); | |
96 | #endif /* CONFIG_NO_HOSTAPD_LOGGER */ | |
97 | } | |
98 | ||
99 | ||
100 | static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr, | |
101 | u16 reason) | |
102 | { | |
103 | struct hostapd_data *hapd = ctx; | |
104 | wpa_printf(MSG_DEBUG, "%s: WPA authenticator requests disconnect: " | |
105 | "STA " MACSTR " reason %d", | |
106 | __func__, MAC2STR(addr), reason); | |
107 | ap_sta_disconnect(hapd, NULL, addr, reason); | |
108 | } | |
109 | ||
110 | ||
111 | static void hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr) | |
112 | { | |
113 | struct hostapd_data *hapd = ctx; | |
114 | michael_mic_failure(hapd, addr, 0); | |
115 | } | |
116 | ||
117 | ||
118 | static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr, | |
119 | wpa_eapol_variable var, int value) | |
120 | { | |
121 | struct hostapd_data *hapd = ctx; | |
122 | struct sta_info *sta = ap_get_sta(hapd, addr); | |
123 | if (sta == NULL) | |
124 | return; | |
125 | switch (var) { | |
126 | case WPA_EAPOL_portEnabled: | |
127 | ieee802_1x_notify_port_enabled(sta->eapol_sm, value); | |
128 | break; | |
129 | case WPA_EAPOL_portValid: | |
130 | ieee802_1x_notify_port_valid(sta->eapol_sm, value); | |
131 | break; | |
132 | case WPA_EAPOL_authorized: | |
133 | ieee802_1x_set_sta_authorized(hapd, sta, value); | |
134 | break; | |
135 | case WPA_EAPOL_portControl_Auto: | |
136 | if (sta->eapol_sm) | |
137 | sta->eapol_sm->portControl = Auto; | |
138 | break; | |
139 | case WPA_EAPOL_keyRun: | |
140 | if (sta->eapol_sm) | |
141 | sta->eapol_sm->keyRun = value ? TRUE : FALSE; | |
142 | break; | |
143 | case WPA_EAPOL_keyAvailable: | |
144 | if (sta->eapol_sm) | |
145 | sta->eapol_sm->eap_if->eapKeyAvailable = | |
146 | value ? TRUE : FALSE; | |
147 | break; | |
148 | case WPA_EAPOL_keyDone: | |
149 | if (sta->eapol_sm) | |
150 | sta->eapol_sm->keyDone = value ? TRUE : FALSE; | |
151 | break; | |
152 | case WPA_EAPOL_inc_EapolFramesTx: | |
153 | if (sta->eapol_sm) | |
154 | sta->eapol_sm->dot1xAuthEapolFramesTx++; | |
155 | break; | |
156 | } | |
157 | } | |
158 | ||
159 | ||
160 | static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr, | |
161 | wpa_eapol_variable var) | |
162 | { | |
163 | struct hostapd_data *hapd = ctx; | |
164 | struct sta_info *sta = ap_get_sta(hapd, addr); | |
165 | if (sta == NULL || sta->eapol_sm == NULL) | |
166 | return -1; | |
167 | switch (var) { | |
168 | case WPA_EAPOL_keyRun: | |
169 | return sta->eapol_sm->keyRun; | |
170 | case WPA_EAPOL_keyAvailable: | |
171 | return sta->eapol_sm->eap_if->eapKeyAvailable; | |
172 | default: | |
173 | return -1; | |
174 | } | |
175 | } | |
176 | ||
177 | ||
178 | static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, | |
179 | const u8 *prev_psk) | |
180 | { | |
181 | struct hostapd_data *hapd = ctx; | |
182 | return hostapd_get_psk(hapd->conf, addr, prev_psk); | |
183 | } | |
184 | ||
185 | ||
186 | static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk, | |
187 | size_t *len) | |
188 | { | |
189 | struct hostapd_data *hapd = ctx; | |
190 | const u8 *key; | |
191 | size_t keylen; | |
192 | struct sta_info *sta; | |
193 | ||
194 | sta = ap_get_sta(hapd, addr); | |
195 | if (sta == NULL) | |
196 | return -1; | |
197 | ||
198 | key = ieee802_1x_get_key(sta->eapol_sm, &keylen); | |
199 | if (key == NULL) | |
200 | return -1; | |
201 | ||
202 | if (keylen > *len) | |
203 | keylen = *len; | |
204 | os_memcpy(msk, key, keylen); | |
205 | *len = keylen; | |
206 | ||
207 | return 0; | |
208 | } | |
209 | ||
210 | ||
211 | static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, wpa_alg alg, | |
212 | const u8 *addr, int idx, u8 *key, | |
213 | size_t key_len) | |
214 | { | |
215 | struct hostapd_data *hapd = ctx; | |
216 | const char *ifname = hapd->conf->iface; | |
217 | ||
218 | if (vlan_id > 0) { | |
219 | ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); | |
220 | if (ifname == NULL) | |
221 | return -1; | |
222 | } | |
223 | ||
224 | return hapd->drv.set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0, | |
225 | key, key_len); | |
226 | } | |
227 | ||
228 | ||
229 | static int hostapd_wpa_auth_get_seqnum(void *ctx, const u8 *addr, int idx, | |
230 | u8 *seq) | |
231 | { | |
232 | struct hostapd_data *hapd = ctx; | |
233 | return hostapd_get_seqnum(hapd->conf->iface, hapd, addr, idx, seq); | |
234 | } | |
235 | ||
236 | ||
237 | static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr, | |
238 | const u8 *data, size_t data_len, | |
239 | int encrypt) | |
240 | { | |
241 | struct hostapd_data *hapd = ctx; | |
242 | return hapd->drv.send_eapol(hapd, addr, data, data_len, encrypt); | |
243 | } | |
244 | ||
245 | ||
246 | static int hostapd_wpa_auth_for_each_sta( | |
247 | void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx), | |
248 | void *cb_ctx) | |
249 | { | |
250 | struct hostapd_data *hapd = ctx; | |
251 | struct sta_info *sta; | |
252 | ||
253 | for (sta = hapd->sta_list; sta; sta = sta->next) { | |
254 | if (sta->wpa_sm && cb(sta->wpa_sm, cb_ctx)) | |
255 | return 1; | |
256 | } | |
257 | return 0; | |
258 | } | |
259 | ||
260 | ||
261 | struct wpa_auth_iface_iter_data { | |
262 | int (*cb)(struct wpa_authenticator *sm, void *ctx); | |
263 | void *cb_ctx; | |
264 | }; | |
265 | ||
266 | static int wpa_auth_iface_iter(struct hostapd_iface *iface, void *ctx) | |
267 | { | |
268 | struct wpa_auth_iface_iter_data *data = ctx; | |
269 | size_t i; | |
270 | for (i = 0; i < iface->num_bss; i++) { | |
271 | if (data->cb(iface->bss[i]->wpa_auth, data->cb_ctx)) | |
272 | return 1; | |
273 | } | |
274 | return 0; | |
275 | } | |
276 | ||
277 | ||
278 | static int hostapd_wpa_auth_for_each_auth( | |
279 | void *ctx, int (*cb)(struct wpa_authenticator *sm, void *ctx), | |
280 | void *cb_ctx) | |
281 | { | |
282 | struct hostapd_data *hapd = ctx; | |
283 | struct wpa_auth_iface_iter_data data; | |
1b56c26c JM |
284 | if (hapd->iface->for_each_interface == NULL) |
285 | return -1; | |
c442055e JM |
286 | data.cb = cb; |
287 | data.cb_ctx = cb_ctx; | |
1b56c26c JM |
288 | return hapd->iface->for_each_interface(hapd->iface->interfaces, |
289 | wpa_auth_iface_iter, &data); | |
c442055e JM |
290 | } |
291 | ||
292 | ||
293 | static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, | |
294 | const u8 *data, size_t data_len) | |
295 | { | |
296 | struct hostapd_data *hapd = ctx; | |
297 | ||
298 | if (hapd->driver && hapd->driver->send_ether) | |
299 | return hapd->driver->send_ether(hapd->drv_priv, dst, | |
300 | hapd->own_addr, proto, | |
301 | data, data_len); | |
302 | if (hapd->l2 == NULL) | |
303 | return -1; | |
304 | return l2_packet_send(hapd->l2, dst, proto, data, data_len); | |
305 | } | |
306 | ||
307 | ||
308 | #ifdef CONFIG_IEEE80211R | |
309 | ||
310 | static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, | |
311 | const u8 *data, size_t data_len) | |
312 | { | |
313 | struct hostapd_data *hapd = ctx; | |
314 | int res; | |
315 | struct ieee80211_mgmt *m; | |
316 | size_t mlen; | |
317 | struct sta_info *sta; | |
318 | ||
319 | sta = ap_get_sta(hapd, dst); | |
320 | if (sta == NULL || sta->wpa_sm == NULL) | |
321 | return -1; | |
322 | ||
323 | m = os_zalloc(sizeof(*m) + data_len); | |
324 | if (m == NULL) | |
325 | return -1; | |
326 | mlen = ((u8 *) &m->u - (u8 *) m) + data_len; | |
327 | m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, | |
328 | WLAN_FC_STYPE_ACTION); | |
329 | os_memcpy(m->da, dst, ETH_ALEN); | |
330 | os_memcpy(m->sa, hapd->own_addr, ETH_ALEN); | |
331 | os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); | |
332 | os_memcpy(&m->u, data, data_len); | |
333 | ||
334 | res = hapd->drv.send_mgmt_frame(hapd, (u8 *) m, mlen); | |
335 | os_free(m); | |
336 | return res; | |
337 | } | |
338 | ||
339 | ||
340 | static struct wpa_state_machine * | |
341 | hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) | |
342 | { | |
343 | struct hostapd_data *hapd = ctx; | |
344 | struct sta_info *sta; | |
345 | ||
346 | sta = ap_sta_add(hapd, sta_addr); | |
347 | if (sta == NULL) | |
348 | return NULL; | |
349 | if (sta->wpa_sm) | |
350 | return sta->wpa_sm; | |
351 | ||
352 | sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr); | |
353 | if (sta->wpa_sm == NULL) { | |
354 | ap_free_sta(hapd, sta); | |
355 | return NULL; | |
356 | } | |
357 | sta->auth_alg = WLAN_AUTH_FT; | |
358 | ||
359 | return sta->wpa_sm; | |
360 | } | |
361 | ||
362 | ||
363 | static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, | |
364 | size_t len) | |
365 | { | |
366 | struct hostapd_data *hapd = ctx; | |
367 | wpa_ft_rrb_rx(hapd->wpa_auth, src_addr, buf, len); | |
368 | } | |
369 | ||
370 | #endif /* CONFIG_IEEE80211R */ | |
371 | ||
372 | ||
373 | int hostapd_setup_wpa(struct hostapd_data *hapd) | |
374 | { | |
375 | struct wpa_auth_config _conf; | |
376 | struct wpa_auth_callbacks cb; | |
377 | const u8 *wpa_ie; | |
378 | size_t wpa_ie_len; | |
379 | ||
380 | hostapd_wpa_auth_conf(hapd->conf, &_conf); | |
381 | os_memset(&cb, 0, sizeof(cb)); | |
382 | cb.ctx = hapd; | |
383 | cb.logger = hostapd_wpa_auth_logger; | |
384 | cb.disconnect = hostapd_wpa_auth_disconnect; | |
385 | cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report; | |
386 | cb.set_eapol = hostapd_wpa_auth_set_eapol; | |
387 | cb.get_eapol = hostapd_wpa_auth_get_eapol; | |
388 | cb.get_psk = hostapd_wpa_auth_get_psk; | |
389 | cb.get_msk = hostapd_wpa_auth_get_msk; | |
390 | cb.set_key = hostapd_wpa_auth_set_key; | |
391 | cb.get_seqnum = hostapd_wpa_auth_get_seqnum; | |
392 | cb.send_eapol = hostapd_wpa_auth_send_eapol; | |
393 | cb.for_each_sta = hostapd_wpa_auth_for_each_sta; | |
394 | cb.for_each_auth = hostapd_wpa_auth_for_each_auth; | |
395 | cb.send_ether = hostapd_wpa_auth_send_ether; | |
396 | #ifdef CONFIG_IEEE80211R | |
397 | cb.send_ft_action = hostapd_wpa_auth_send_ft_action; | |
398 | cb.add_sta = hostapd_wpa_auth_add_sta; | |
399 | #endif /* CONFIG_IEEE80211R */ | |
400 | hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb); | |
401 | if (hapd->wpa_auth == NULL) { | |
402 | wpa_printf(MSG_ERROR, "WPA initialization failed."); | |
403 | return -1; | |
404 | } | |
405 | ||
406 | if (hostapd_set_privacy(hapd, 1)) { | |
407 | wpa_printf(MSG_ERROR, "Could not set PrivacyInvoked " | |
408 | "for interface %s", hapd->conf->iface); | |
409 | return -1; | |
410 | } | |
411 | ||
412 | wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len); | |
413 | if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len)) { | |
414 | wpa_printf(MSG_ERROR, "Failed to configure WPA IE for " | |
415 | "the kernel driver."); | |
416 | return -1; | |
417 | } | |
418 | ||
419 | if (rsn_preauth_iface_init(hapd)) { | |
420 | wpa_printf(MSG_ERROR, "Initialization of RSN " | |
421 | "pre-authentication failed."); | |
422 | return -1; | |
423 | } | |
424 | ||
425 | #ifdef CONFIG_IEEE80211R | |
426 | if (!hostapd_drv_none(hapd)) { | |
427 | hapd->l2 = l2_packet_init(hapd->conf->iface, NULL, ETH_P_RRB, | |
428 | hostapd_rrb_receive, hapd, 0); | |
429 | if (hapd->l2 == NULL && | |
430 | (hapd->driver == NULL || | |
431 | hapd->driver->send_ether == NULL)) { | |
432 | wpa_printf(MSG_ERROR, "Failed to open l2_packet " | |
433 | "interface"); | |
434 | return -1; | |
435 | } | |
436 | } | |
437 | #endif /* CONFIG_IEEE80211R */ | |
438 | ||
439 | return 0; | |
440 | ||
441 | } | |
442 | ||
443 | ||
444 | void hostapd_reconfig_wpa(struct hostapd_data *hapd) | |
445 | { | |
446 | struct wpa_auth_config wpa_auth_conf; | |
447 | hostapd_wpa_auth_conf(hapd->conf, &wpa_auth_conf); | |
448 | wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf); | |
449 | } | |
450 | ||
451 | ||
452 | void hostapd_deinit_wpa(struct hostapd_data *hapd) | |
453 | { | |
454 | rsn_preauth_iface_deinit(hapd); | |
455 | if (hapd->wpa_auth) { | |
456 | wpa_deinit(hapd->wpa_auth); | |
457 | hapd->wpa_auth = NULL; | |
458 | ||
459 | if (hostapd_set_privacy(hapd, 0)) { | |
460 | wpa_printf(MSG_DEBUG, "Could not disable " | |
461 | "PrivacyInvoked for interface %s", | |
462 | hapd->conf->iface); | |
463 | } | |
464 | ||
465 | if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { | |
466 | wpa_printf(MSG_DEBUG, "Could not remove generic " | |
467 | "information element from interface %s", | |
468 | hapd->conf->iface); | |
469 | } | |
470 | } | |
471 | ieee802_1x_deinit(hapd); | |
472 | ||
473 | #ifdef CONFIG_IEEE80211R | |
474 | l2_packet_deinit(hapd->l2); | |
475 | #endif /* CONFIG_IEEE80211R */ | |
476 | } |