]>
Commit | Line | Data |
---|---|---|
11ef8d35 JM |
1 | /* |
2 | * wpa_supplicant - IBSS RSN | |
e743db43 | 3 | * Copyright (c) 2009-2013, Jouni Malinen <j@w1.fi> |
11ef8d35 | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
11ef8d35 JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #include "common.h" | |
e743db43 | 12 | #include "common/wpa_ctrl.h" |
add7add0 | 13 | #include "utils/eloop.h" |
e2d492dd | 14 | #include "l2_packet/l2_packet.h" |
3acb5005 JM |
15 | #include "rsn_supp/wpa.h" |
16 | #include "rsn_supp/wpa_ie.h" | |
71934751 | 17 | #include "ap/wpa_auth.h" |
1057d78e | 18 | #include "wpa_supplicant_i.h" |
ea038e0d | 19 | #include "driver_i.h" |
13adc57b | 20 | #include "common/ieee802_11_defs.h" |
11ef8d35 JM |
21 | #include "ibss_rsn.h" |
22 | ||
23 | ||
add7add0 JM |
24 | static void ibss_rsn_auth_timeout(void *eloop_ctx, void *timeout_ctx); |
25 | ||
26 | ||
fa7187bf AQ |
27 | static struct ibss_rsn_peer * ibss_rsn_get_peer(struct ibss_rsn *ibss_rsn, |
28 | const u8 *addr) | |
29 | { | |
30 | struct ibss_rsn_peer *peer; | |
31 | ||
32 | for (peer = ibss_rsn->peers; peer; peer = peer->next) | |
33 | if (os_memcmp(addr, peer->addr, ETH_ALEN) == 0) | |
34 | break; | |
35 | return peer; | |
36 | } | |
37 | ||
38 | ||
11ef8d35 JM |
39 | static void ibss_rsn_free(struct ibss_rsn_peer *peer) |
40 | { | |
add7add0 | 41 | eloop_cancel_timeout(ibss_rsn_auth_timeout, peer, NULL); |
11ef8d35 JM |
42 | wpa_auth_sta_deinit(peer->auth); |
43 | wpa_sm_deinit(peer->supp); | |
44 | os_free(peer); | |
45 | } | |
46 | ||
47 | ||
71934751 | 48 | static void supp_set_state(void *ctx, enum wpa_states state) |
3146b2b9 JM |
49 | { |
50 | struct ibss_rsn_peer *peer = ctx; | |
51 | peer->supp_state = state; | |
52 | } | |
53 | ||
54 | ||
f385833b XC |
55 | static enum wpa_states supp_get_state(void *ctx) |
56 | { | |
57 | struct ibss_rsn_peer *peer = ctx; | |
58 | return peer->supp_state; | |
59 | } | |
60 | ||
61 | ||
11ef8d35 JM |
62 | static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf, |
63 | size_t len) | |
64 | { | |
e2d492dd JM |
65 | struct ibss_rsn_peer *peer = ctx; |
66 | struct wpa_supplicant *wpa_s = peer->ibss_rsn->wpa_s; | |
11ef8d35 JM |
67 | |
68 | wpa_printf(MSG_DEBUG, "SUPP: %s(dest=" MACSTR " proto=0x%04x " | |
69 | "len=%lu)", | |
70 | __func__, MAC2STR(dest), proto, (unsigned long) len); | |
71 | ||
e2d492dd JM |
72 | if (wpa_s->l2) |
73 | return l2_packet_send(wpa_s->l2, dest, proto, buf, len); | |
11ef8d35 | 74 | |
2961bfa8 | 75 | return -1; |
11ef8d35 JM |
76 | } |
77 | ||
78 | ||
79 | static u8 * supp_alloc_eapol(void *ctx, u8 type, const void *data, | |
80 | u16 data_len, size_t *msg_len, void **data_pos) | |
81 | { | |
82 | struct ieee802_1x_hdr *hdr; | |
83 | ||
84 | wpa_printf(MSG_DEBUG, "SUPP: %s(type=%d data_len=%d)", | |
85 | __func__, type, data_len); | |
86 | ||
87 | *msg_len = sizeof(*hdr) + data_len; | |
88 | hdr = os_malloc(*msg_len); | |
89 | if (hdr == NULL) | |
90 | return NULL; | |
91 | ||
92 | hdr->version = 2; | |
93 | hdr->type = type; | |
94 | hdr->length = host_to_be16(data_len); | |
95 | ||
96 | if (data) | |
97 | os_memcpy(hdr + 1, data, data_len); | |
98 | else | |
99 | os_memset(hdr + 1, 0, data_len); | |
100 | ||
101 | if (data_pos) | |
102 | *data_pos = hdr + 1; | |
103 | ||
104 | return (u8 *) hdr; | |
105 | } | |
106 | ||
107 | ||
108 | static int supp_get_beacon_ie(void *ctx) | |
109 | { | |
4691fc7b JM |
110 | struct ibss_rsn_peer *peer = ctx; |
111 | ||
11ef8d35 | 112 | wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); |
4691fc7b JM |
113 | /* TODO: get correct RSN IE */ |
114 | return wpa_sm_set_ap_rsn_ie(peer->supp, | |
115 | (u8 *) "\x30\x14\x01\x00" | |
116 | "\x00\x0f\xac\x04" | |
117 | "\x01\x00\x00\x0f\xac\x04" | |
118 | "\x01\x00\x00\x0f\xac\x02" | |
119 | "\x00\x00", 22); | |
11ef8d35 JM |
120 | } |
121 | ||
122 | ||
e743db43 JM |
123 | static void ibss_check_rsn_completed(struct ibss_rsn_peer *peer) |
124 | { | |
125 | struct wpa_supplicant *wpa_s = peer->ibss_rsn->wpa_s; | |
126 | ||
127 | if ((peer->authentication_status & | |
128 | (IBSS_RSN_SET_PTK_SUPP | IBSS_RSN_SET_PTK_AUTH)) != | |
129 | (IBSS_RSN_SET_PTK_SUPP | IBSS_RSN_SET_PTK_AUTH)) | |
130 | return; | |
131 | if (peer->authentication_status & IBSS_RSN_REPORTED_PTK) | |
132 | return; | |
133 | peer->authentication_status |= IBSS_RSN_REPORTED_PTK; | |
134 | wpa_msg(wpa_s, MSG_INFO, IBSS_RSN_COMPLETED MACSTR, | |
135 | MAC2STR(peer->addr)); | |
136 | } | |
137 | ||
138 | ||
71934751 | 139 | static int supp_set_key(void *ctx, enum wpa_alg alg, |
11ef8d35 JM |
140 | const u8 *addr, int key_idx, int set_tx, |
141 | const u8 *seq, size_t seq_len, | |
142 | const u8 *key, size_t key_len) | |
143 | { | |
98f14041 JM |
144 | struct ibss_rsn_peer *peer = ctx; |
145 | ||
a486c0c7 JM |
146 | wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d " |
147 | "set_tx=%d)", | |
148 | __func__, alg, MAC2STR(addr), key_idx, set_tx); | |
149 | wpa_hexdump(MSG_DEBUG, "SUPP: set_key - seq", seq, seq_len); | |
150 | wpa_hexdump_key(MSG_DEBUG, "SUPP: set_key - key", key, key_len); | |
151 | ||
98f14041 | 152 | if (key_idx == 0) { |
e743db43 JM |
153 | peer->authentication_status |= IBSS_RSN_SET_PTK_SUPP; |
154 | ibss_check_rsn_completed(peer); | |
98f14041 JM |
155 | /* |
156 | * In IBSS RSN, the pairwise key from the 4-way handshake | |
157 | * initiated by the peer with highest MAC address is used. | |
158 | */ | |
159 | if (os_memcmp(peer->ibss_rsn->wpa_s->own_addr, peer->addr, | |
a486c0c7 JM |
160 | ETH_ALEN) > 0) { |
161 | wpa_printf(MSG_DEBUG, "SUPP: Do not use this PTK"); | |
98f14041 | 162 | return 0; |
a486c0c7 | 163 | } |
98f14041 JM |
164 | } |
165 | ||
4a26df71 | 166 | if (is_broadcast_ether_addr(addr)) |
89c38e32 | 167 | addr = peer->addr; |
98f14041 JM |
168 | return wpa_drv_set_key(peer->ibss_rsn->wpa_s, alg, addr, key_idx, |
169 | set_tx, seq, seq_len, key, key_len); | |
11ef8d35 JM |
170 | } |
171 | ||
172 | ||
3146b2b9 JM |
173 | static void * supp_get_network_ctx(void *ctx) |
174 | { | |
175 | struct ibss_rsn_peer *peer = ctx; | |
176 | return wpa_supplicant_get_ssid(peer->ibss_rsn->wpa_s); | |
177 | } | |
178 | ||
179 | ||
11ef8d35 JM |
180 | static int supp_mlme_setprotection(void *ctx, const u8 *addr, |
181 | int protection_type, int key_type) | |
182 | { | |
183 | wpa_printf(MSG_DEBUG, "SUPP: %s(addr=" MACSTR " protection_type=%d " | |
184 | "key_type=%d)", | |
185 | __func__, MAC2STR(addr), protection_type, key_type); | |
186 | return 0; | |
187 | } | |
188 | ||
189 | ||
190 | static void supp_cancel_auth_timeout(void *ctx) | |
191 | { | |
192 | wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); | |
193 | } | |
194 | ||
195 | ||
89c38e32 JM |
196 | static void supp_deauthenticate(void * ctx, int reason_code) |
197 | { | |
198 | wpa_printf(MSG_DEBUG, "SUPP: %s (TODO)", __func__); | |
199 | } | |
200 | ||
201 | ||
19df9b07 JM |
202 | static int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr, |
203 | const u8 *psk) | |
11ef8d35 JM |
204 | { |
205 | struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx)); | |
206 | if (ctx == NULL) | |
207 | return -1; | |
208 | ||
209 | ctx->ctx = peer; | |
0f057fb2 | 210 | ctx->msg_ctx = peer->ibss_rsn->wpa_s; |
3146b2b9 | 211 | ctx->set_state = supp_set_state; |
f385833b | 212 | ctx->get_state = supp_get_state; |
11ef8d35 JM |
213 | ctx->ether_send = supp_ether_send; |
214 | ctx->get_beacon_ie = supp_get_beacon_ie; | |
215 | ctx->alloc_eapol = supp_alloc_eapol; | |
216 | ctx->set_key = supp_set_key; | |
3146b2b9 | 217 | ctx->get_network_ctx = supp_get_network_ctx; |
11ef8d35 JM |
218 | ctx->mlme_setprotection = supp_mlme_setprotection; |
219 | ctx->cancel_auth_timeout = supp_cancel_auth_timeout; | |
89c38e32 | 220 | ctx->deauthenticate = supp_deauthenticate; |
11ef8d35 JM |
221 | peer->supp = wpa_sm_init(ctx); |
222 | if (peer->supp == NULL) { | |
223 | wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed"); | |
224 | return -1; | |
225 | } | |
226 | ||
227 | wpa_sm_set_own_addr(peer->supp, own_addr); | |
228 | wpa_sm_set_param(peer->supp, WPA_PARAM_RSN_ENABLED, 1); | |
229 | wpa_sm_set_param(peer->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN); | |
230 | wpa_sm_set_param(peer->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP); | |
231 | wpa_sm_set_param(peer->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP); | |
232 | wpa_sm_set_param(peer->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK); | |
bc26ac50 | 233 | wpa_sm_set_pmk(peer->supp, psk, PMK_LEN, NULL); |
11ef8d35 | 234 | |
11ef8d35 JM |
235 | peer->supp_ie_len = sizeof(peer->supp_ie); |
236 | if (wpa_sm_set_assoc_wpa_ie_default(peer->supp, peer->supp_ie, | |
237 | &peer->supp_ie_len) < 0) { | |
238 | wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()" | |
239 | " failed"); | |
240 | return -1; | |
241 | } | |
11ef8d35 JM |
242 | |
243 | wpa_sm_notify_assoc(peer->supp, peer->addr); | |
244 | ||
245 | return 0; | |
246 | } | |
247 | ||
248 | ||
249 | static void auth_logger(void *ctx, const u8 *addr, logger_level level, | |
250 | const char *txt) | |
251 | { | |
252 | if (addr) | |
253 | wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s", | |
254 | MAC2STR(addr), txt); | |
255 | else | |
256 | wpa_printf(MSG_DEBUG, "AUTH: %s", txt); | |
257 | } | |
258 | ||
259 | ||
759fd76b JM |
260 | static const u8 * auth_get_psk(void *ctx, const u8 *addr, |
261 | const u8 *p2p_dev_addr, const u8 *prev_psk) | |
11ef8d35 JM |
262 | { |
263 | struct ibss_rsn *ibss_rsn = ctx; | |
264 | wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", | |
265 | __func__, MAC2STR(addr), prev_psk); | |
266 | if (prev_psk) | |
267 | return NULL; | |
268 | return ibss_rsn->psk; | |
269 | } | |
270 | ||
271 | ||
272 | static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data, | |
273 | size_t data_len, int encrypt) | |
274 | { | |
da3c6a5e JM |
275 | struct ibss_rsn *ibss_rsn = ctx; |
276 | struct wpa_supplicant *wpa_s = ibss_rsn->wpa_s; | |
11ef8d35 JM |
277 | |
278 | wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu " | |
279 | "encrypt=%d)", | |
280 | __func__, MAC2STR(addr), (unsigned long) data_len, encrypt); | |
281 | ||
e2d492dd JM |
282 | if (wpa_s->l2) |
283 | return l2_packet_send(wpa_s->l2, addr, ETH_P_EAPOL, data, | |
284 | data_len); | |
11ef8d35 | 285 | |
2961bfa8 | 286 | return -1; |
11ef8d35 JM |
287 | } |
288 | ||
289 | ||
71934751 JM |
290 | static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, |
291 | const u8 *addr, int idx, u8 *key, size_t key_len) | |
98f14041 JM |
292 | { |
293 | struct ibss_rsn *ibss_rsn = ctx; | |
294 | u8 seq[6]; | |
98f14041 JM |
295 | |
296 | os_memset(seq, 0, sizeof(seq)); | |
a486c0c7 JM |
297 | |
298 | if (addr) { | |
299 | wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d addr=" MACSTR | |
300 | " key_idx=%d)", | |
301 | __func__, alg, MAC2STR(addr), idx); | |
302 | } else { | |
303 | wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d key_idx=%d)", | |
304 | __func__, alg, idx); | |
305 | } | |
306 | wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len); | |
307 | ||
98f14041 | 308 | if (idx == 0) { |
e743db43 JM |
309 | if (addr) { |
310 | struct ibss_rsn_peer *peer; | |
311 | peer = ibss_rsn_get_peer(ibss_rsn, addr); | |
312 | if (peer) { | |
313 | peer->authentication_status |= | |
314 | IBSS_RSN_SET_PTK_AUTH; | |
315 | ibss_check_rsn_completed(peer); | |
316 | } | |
317 | } | |
98f14041 JM |
318 | /* |
319 | * In IBSS RSN, the pairwise key from the 4-way handshake | |
320 | * initiated by the peer with highest MAC address is used. | |
321 | */ | |
a416fb47 JM |
322 | if (addr == NULL || |
323 | os_memcmp(ibss_rsn->wpa_s->own_addr, addr, ETH_ALEN) < 0) { | |
a486c0c7 | 324 | wpa_printf(MSG_DEBUG, "AUTH: Do not use this PTK"); |
98f14041 | 325 | return 0; |
a486c0c7 | 326 | } |
98f14041 JM |
327 | } |
328 | ||
329 | return wpa_drv_set_key(ibss_rsn->wpa_s, alg, addr, idx, | |
330 | 1, seq, 6, key, key_len); | |
331 | } | |
332 | ||
333 | ||
1aef400b BB |
334 | static void ibss_rsn_disconnect(void *ctx, const u8 *addr, u16 reason) |
335 | { | |
336 | struct ibss_rsn *ibss_rsn = ctx; | |
337 | wpa_drv_sta_deauth(ibss_rsn->wpa_s, addr, reason); | |
338 | } | |
339 | ||
340 | ||
92ac6376 XC |
341 | static int auth_for_each_sta(void *ctx, int (*cb)(struct wpa_state_machine *sm, |
342 | void *ctx), | |
343 | void *cb_ctx) | |
344 | { | |
345 | struct ibss_rsn *ibss_rsn = ctx; | |
346 | struct ibss_rsn_peer *peer; | |
347 | ||
348 | wpa_printf(MSG_DEBUG, "AUTH: for_each_sta"); | |
349 | ||
350 | for (peer = ibss_rsn->peers; peer; peer = peer->next) { | |
351 | if (peer->auth && cb(peer->auth, cb_ctx)) | |
352 | return 1; | |
353 | } | |
354 | ||
355 | return 0; | |
356 | } | |
357 | ||
358 | ||
e640888c AQ |
359 | static void ibss_set_sta_authorized(struct ibss_rsn *ibss_rsn, |
360 | struct ibss_rsn_peer *peer, int authorized) | |
361 | { | |
362 | int res; | |
363 | ||
364 | if (authorized) { | |
365 | res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr, | |
366 | WPA_STA_AUTHORIZED, | |
367 | WPA_STA_AUTHORIZED, ~0); | |
368 | wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " authorizing port", | |
369 | MAC2STR(peer->addr)); | |
370 | } else { | |
371 | res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr, | |
372 | 0, 0, ~WPA_STA_AUTHORIZED); | |
373 | wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " unauthorizing port", | |
374 | MAC2STR(peer->addr)); | |
375 | } | |
376 | ||
377 | if (res && errno != ENOENT) { | |
378 | wpa_printf(MSG_DEBUG, "Could not set station " MACSTR " flags " | |
379 | "for kernel driver (errno=%d)", | |
380 | MAC2STR(peer->addr), errno); | |
381 | } | |
382 | } | |
383 | ||
384 | ||
385 | static void auth_set_eapol(void *ctx, const u8 *addr, | |
386 | wpa_eapol_variable var, int value) | |
387 | { | |
388 | struct ibss_rsn *ibss_rsn = ctx; | |
389 | struct ibss_rsn_peer *peer = ibss_rsn_get_peer(ibss_rsn, addr); | |
390 | ||
391 | if (peer == NULL) | |
392 | return; | |
393 | ||
394 | switch (var) { | |
395 | case WPA_EAPOL_authorized: | |
396 | ibss_set_sta_authorized(ibss_rsn, peer, value); | |
397 | break; | |
398 | default: | |
399 | /* do not handle any other event */ | |
400 | wpa_printf(MSG_DEBUG, "AUTH: eapol event not handled %d", var); | |
401 | break; | |
402 | } | |
403 | } | |
404 | ||
405 | ||
11ef8d35 JM |
406 | static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, |
407 | const u8 *own_addr) | |
408 | { | |
409 | struct wpa_auth_config conf; | |
410 | struct wpa_auth_callbacks cb; | |
411 | ||
412 | wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine"); | |
413 | ||
414 | os_memset(&conf, 0, sizeof(conf)); | |
415 | conf.wpa = 2; | |
416 | conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK; | |
417 | conf.wpa_pairwise = WPA_CIPHER_CCMP; | |
418 | conf.rsn_pairwise = WPA_CIPHER_CCMP; | |
419 | conf.wpa_group = WPA_CIPHER_CCMP; | |
420 | conf.eapol_version = 2; | |
13579b98 | 421 | conf.wpa_group_rekey = 600; |
11ef8d35 JM |
422 | |
423 | os_memset(&cb, 0, sizeof(cb)); | |
424 | cb.ctx = ibss_rsn; | |
425 | cb.logger = auth_logger; | |
e640888c | 426 | cb.set_eapol = auth_set_eapol; |
11ef8d35 JM |
427 | cb.send_eapol = auth_send_eapol; |
428 | cb.get_psk = auth_get_psk; | |
98f14041 | 429 | cb.set_key = auth_set_key; |
92ac6376 | 430 | cb.for_each_sta = auth_for_each_sta; |
1aef400b | 431 | cb.disconnect = ibss_rsn_disconnect; |
11ef8d35 JM |
432 | |
433 | ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb); | |
434 | if (ibss_rsn->auth_group == NULL) { | |
435 | wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed"); | |
436 | return -1; | |
437 | } | |
438 | ||
457a126e JB |
439 | wpa_init_keys(ibss_rsn->auth_group); |
440 | ||
11ef8d35 JM |
441 | return 0; |
442 | } | |
443 | ||
444 | ||
445 | static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn, | |
446 | struct ibss_rsn_peer *peer) | |
447 | { | |
94ddef3e | 448 | peer->auth = wpa_auth_sta_init(ibss_rsn->auth_group, peer->addr, NULL); |
11ef8d35 JM |
449 | if (peer->auth == NULL) { |
450 | wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed"); | |
451 | return -1; | |
452 | } | |
453 | ||
d0fc6e12 JM |
454 | /* TODO: get peer RSN IE with Probe Request */ |
455 | if (wpa_validate_wpa_ie(ibss_rsn->auth_group, peer->auth, | |
4691fc7b | 456 | (u8 *) "\x30\x14\x01\x00" |
d0fc6e12 JM |
457 | "\x00\x0f\xac\x04" |
458 | "\x01\x00\x00\x0f\xac\x04" | |
4691fc7b JM |
459 | "\x01\x00\x00\x0f\xac\x02" |
460 | "\x00\x00", 22, NULL, 0) != | |
d0fc6e12 | 461 | WPA_IE_OK) { |
11ef8d35 JM |
462 | wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed"); |
463 | return -1; | |
464 | } | |
11ef8d35 | 465 | |
6f9b5d16 JM |
466 | if (wpa_auth_sm_event(peer->auth, WPA_ASSOC)) |
467 | return -1; | |
11ef8d35 | 468 | |
6f9b5d16 JM |
469 | if (wpa_auth_sta_associated(ibss_rsn->auth_group, peer->auth)) |
470 | return -1; | |
11ef8d35 JM |
471 | |
472 | return 0; | |
473 | } | |
474 | ||
475 | ||
13adc57b | 476 | static int ibss_rsn_send_auth(struct ibss_rsn *ibss_rsn, const u8 *da, int seq) |
11ef8d35 | 477 | { |
13adc57b AQ |
478 | struct ieee80211_mgmt auth; |
479 | const size_t auth_length = IEEE80211_HDRLEN + sizeof(auth.u.auth); | |
480 | struct wpa_supplicant *wpa_s = ibss_rsn->wpa_s; | |
11ef8d35 | 481 | |
13adc57b | 482 | if (wpa_s->driver->send_frame == NULL) |
78177a00 JM |
483 | return -1; |
484 | ||
13adc57b AQ |
485 | os_memset(&auth, 0, sizeof(auth)); |
486 | ||
487 | auth.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, | |
488 | WLAN_FC_STYPE_AUTH); | |
489 | os_memcpy(auth.da, da, ETH_ALEN); | |
490 | os_memcpy(auth.sa, wpa_s->own_addr, ETH_ALEN); | |
491 | os_memcpy(auth.bssid, wpa_s->bssid, ETH_ALEN); | |
492 | ||
493 | auth.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN); | |
494 | auth.u.auth.auth_transaction = host_to_le16(seq); | |
495 | auth.u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS); | |
496 | ||
497 | wpa_printf(MSG_DEBUG, "RSN: IBSS TX Auth frame (SEQ %d) to " MACSTR, | |
498 | seq, MAC2STR(da)); | |
499 | ||
500 | return wpa_s->driver->send_frame(wpa_s->drv_priv, (u8 *) &auth, | |
501 | auth_length, 0); | |
502 | } | |
503 | ||
504 | ||
505 | static int ibss_rsn_is_auth_started(struct ibss_rsn_peer * peer) | |
506 | { | |
507 | return peer->authentication_status & | |
508 | (IBSS_RSN_AUTH_BY_US | IBSS_RSN_AUTH_EAPOL_BY_US); | |
509 | } | |
510 | ||
511 | ||
512 | static struct ibss_rsn_peer * | |
513 | ibss_rsn_peer_init(struct ibss_rsn *ibss_rsn, const u8 *addr) | |
514 | { | |
515 | struct ibss_rsn_peer *peer; | |
516 | if (ibss_rsn == NULL) | |
517 | return NULL; | |
518 | ||
519 | peer = ibss_rsn_get_peer(ibss_rsn, addr); | |
520 | if (peer) { | |
521 | wpa_printf(MSG_DEBUG, "RSN: IBSS Supplicant for peer "MACSTR | |
522 | " already running", MAC2STR(addr)); | |
523 | return peer; | |
6c9a98a2 JM |
524 | } |
525 | ||
13adc57b AQ |
526 | wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Supplicant for peer "MACSTR, |
527 | MAC2STR(addr)); | |
11ef8d35 JM |
528 | |
529 | peer = os_zalloc(sizeof(*peer)); | |
13adc57b AQ |
530 | if (peer == NULL) { |
531 | wpa_printf(MSG_DEBUG, "RSN: Could not allocate memory."); | |
532 | return NULL; | |
533 | } | |
11ef8d35 | 534 | |
e2d492dd | 535 | peer->ibss_rsn = ibss_rsn; |
11ef8d35 | 536 | os_memcpy(peer->addr, addr, ETH_ALEN); |
13adc57b | 537 | peer->authentication_status = IBSS_RSN_AUTH_NOT_AUTHENTICATED; |
11ef8d35 | 538 | |
13adc57b AQ |
539 | if (ibss_rsn_supp_init(peer, ibss_rsn->wpa_s->own_addr, |
540 | ibss_rsn->psk) < 0) { | |
11ef8d35 | 541 | ibss_rsn_free(peer); |
13adc57b AQ |
542 | return NULL; |
543 | } | |
544 | ||
545 | peer->next = ibss_rsn->peers; | |
546 | ibss_rsn->peers = peer; | |
547 | ||
548 | return peer; | |
549 | } | |
550 | ||
551 | ||
add7add0 JM |
552 | static void ibss_rsn_auth_timeout(void *eloop_ctx, void *timeout_ctx) |
553 | { | |
554 | struct ibss_rsn_peer *peer = eloop_ctx; | |
555 | ||
556 | /* | |
557 | * Assume peer does not support Authentication exchange or the frame was | |
558 | * lost somewhere - start EAPOL Authenticator. | |
559 | */ | |
560 | wpa_printf(MSG_DEBUG, | |
561 | "RSN: Timeout on waiting Authentication frame response from " | |
562 | MACSTR " - start authenticator", MAC2STR(peer->addr)); | |
563 | ||
564 | peer->authentication_status |= IBSS_RSN_AUTH_BY_US; | |
565 | ibss_rsn_auth_init(peer->ibss_rsn, peer); | |
566 | } | |
567 | ||
568 | ||
13adc57b AQ |
569 | int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr) |
570 | { | |
571 | struct ibss_rsn_peer *peer; | |
572 | int res; | |
573 | ||
574 | /* if the peer already exists, exit immediately */ | |
575 | peer = ibss_rsn_get_peer(ibss_rsn, addr); | |
576 | if (peer) | |
577 | return 0; | |
578 | ||
579 | peer = ibss_rsn_peer_init(ibss_rsn, addr); | |
580 | if (peer == NULL) | |
11ef8d35 | 581 | return -1; |
13adc57b AQ |
582 | |
583 | /* Open Authentication: send first Authentication frame */ | |
584 | res = ibss_rsn_send_auth(ibss_rsn, addr, 1); | |
585 | if (res) { | |
586 | /* | |
587 | * The driver may not support Authentication frame exchange in | |
588 | * IBSS. Ignore authentication and go through EAPOL exchange. | |
589 | */ | |
590 | peer->authentication_status |= IBSS_RSN_AUTH_BY_US; | |
591 | return ibss_rsn_auth_init(ibss_rsn, peer); | |
6fb7b58f | 592 | } else { |
3326f193 | 593 | os_get_reltime(&peer->own_auth_tx); |
add7add0 | 594 | eloop_register_timeout(1, 0, ibss_rsn_auth_timeout, peer, NULL); |
11ef8d35 JM |
595 | } |
596 | ||
13adc57b AQ |
597 | return 0; |
598 | } | |
599 | ||
600 | ||
601 | static int ibss_rsn_peer_authenticated(struct ibss_rsn *ibss_rsn, | |
602 | struct ibss_rsn_peer *peer, int reason) | |
603 | { | |
604 | int already_started; | |
605 | ||
606 | if (ibss_rsn == NULL || peer == NULL) | |
11ef8d35 | 607 | return -1; |
13adc57b AQ |
608 | |
609 | already_started = ibss_rsn_is_auth_started(peer); | |
610 | peer->authentication_status |= reason; | |
611 | ||
612 | if (already_started) { | |
613 | wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator already " | |
614 | "started for peer " MACSTR, MAC2STR(peer->addr)); | |
615 | return 0; | |
11ef8d35 JM |
616 | } |
617 | ||
13adc57b AQ |
618 | wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator " |
619 | "for now-authenticated peer " MACSTR, MAC2STR(peer->addr)); | |
11ef8d35 | 620 | |
13adc57b | 621 | return ibss_rsn_auth_init(ibss_rsn, peer); |
11ef8d35 JM |
622 | } |
623 | ||
624 | ||
ea244d21 XC |
625 | void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac) |
626 | { | |
627 | struct ibss_rsn_peer *peer, *prev; | |
628 | ||
629 | if (ibss_rsn == NULL) | |
630 | return; | |
631 | ||
632 | if (peermac == NULL) { | |
633 | /* remove all peers */ | |
634 | wpa_printf(MSG_DEBUG, "%s: Remove all peers", __func__); | |
635 | peer = ibss_rsn->peers; | |
636 | while (peer) { | |
637 | prev = peer; | |
638 | peer = peer->next; | |
639 | ibss_rsn_free(prev); | |
640 | ibss_rsn->peers = peer; | |
641 | } | |
642 | } else { | |
643 | /* remove specific peer */ | |
644 | wpa_printf(MSG_DEBUG, "%s: Remove specific peer " MACSTR, | |
645 | __func__, MAC2STR(peermac)); | |
646 | ||
647 | for (prev = NULL, peer = ibss_rsn->peers; peer != NULL; | |
648 | prev = peer, peer = peer->next) { | |
649 | if (os_memcmp(peermac, peer->addr, ETH_ALEN) == 0) { | |
650 | if (prev == NULL) | |
651 | ibss_rsn->peers = peer->next; | |
652 | else | |
653 | prev->next = peer->next; | |
654 | ibss_rsn_free(peer); | |
655 | wpa_printf(MSG_DEBUG, "%s: Successfully " | |
656 | "removed a specific peer", | |
657 | __func__); | |
658 | break; | |
659 | } | |
660 | } | |
661 | } | |
662 | } | |
663 | ||
664 | ||
11ef8d35 JM |
665 | struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s) |
666 | { | |
667 | struct ibss_rsn *ibss_rsn; | |
668 | ||
669 | ibss_rsn = os_zalloc(sizeof(*ibss_rsn)); | |
670 | if (ibss_rsn == NULL) | |
671 | return NULL; | |
672 | ibss_rsn->wpa_s = wpa_s; | |
673 | ||
674 | if (ibss_rsn_auth_init_group(ibss_rsn, wpa_s->own_addr) < 0) { | |
675 | ibss_rsn_deinit(ibss_rsn); | |
676 | return NULL; | |
677 | } | |
678 | ||
679 | return ibss_rsn; | |
680 | } | |
681 | ||
682 | ||
683 | void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn) | |
684 | { | |
685 | struct ibss_rsn_peer *peer, *prev; | |
686 | ||
687 | if (ibss_rsn == NULL) | |
688 | return; | |
689 | ||
690 | peer = ibss_rsn->peers; | |
691 | while (peer) { | |
692 | prev = peer; | |
693 | peer = peer->next; | |
694 | ibss_rsn_free(prev); | |
695 | } | |
696 | ||
697 | wpa_deinit(ibss_rsn->auth_group); | |
698 | os_free(ibss_rsn); | |
699 | ||
700 | } | |
8be18440 JM |
701 | |
702 | ||
703 | static int ibss_rsn_eapol_dst_supp(const u8 *buf, size_t len) | |
704 | { | |
705 | const struct ieee802_1x_hdr *hdr; | |
706 | const struct wpa_eapol_key *key; | |
707 | u16 key_info; | |
708 | size_t plen; | |
709 | ||
710 | /* TODO: Support other EAPOL packets than just EAPOL-Key */ | |
711 | ||
712 | if (len < sizeof(*hdr) + sizeof(*key)) | |
713 | return -1; | |
714 | ||
715 | hdr = (const struct ieee802_1x_hdr *) buf; | |
716 | key = (const struct wpa_eapol_key *) (hdr + 1); | |
717 | plen = be_to_host16(hdr->length); | |
718 | ||
719 | if (hdr->version < EAPOL_VERSION) { | |
720 | /* TODO: backwards compatibility */ | |
721 | } | |
722 | if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) { | |
723 | wpa_printf(MSG_DEBUG, "RSN: EAPOL frame (type %u) discarded, " | |
724 | "not a Key frame", hdr->type); | |
725 | return -1; | |
726 | } | |
727 | if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) { | |
728 | wpa_printf(MSG_DEBUG, "RSN: EAPOL frame payload size %lu " | |
729 | "invalid (frame size %lu)", | |
730 | (unsigned long) plen, (unsigned long) len); | |
731 | return -1; | |
732 | } | |
733 | ||
734 | if (key->type != EAPOL_KEY_TYPE_RSN) { | |
735 | wpa_printf(MSG_DEBUG, "RSN: EAPOL-Key type (%d) unknown, " | |
736 | "discarded", key->type); | |
737 | return -1; | |
738 | } | |
739 | ||
740 | key_info = WPA_GET_BE16(key->key_info); | |
741 | ||
742 | return !!(key_info & WPA_KEY_INFO_ACK); | |
743 | } | |
744 | ||
745 | ||
746 | static int ibss_rsn_process_rx_eapol(struct ibss_rsn *ibss_rsn, | |
747 | struct ibss_rsn_peer *peer, | |
748 | const u8 *buf, size_t len) | |
749 | { | |
750 | int supp; | |
751 | u8 *tmp; | |
752 | ||
753 | supp = ibss_rsn_eapol_dst_supp(buf, len); | |
754 | if (supp < 0) | |
755 | return -1; | |
756 | ||
757 | tmp = os_malloc(len); | |
758 | if (tmp == NULL) | |
759 | return -1; | |
760 | os_memcpy(tmp, buf, len); | |
761 | if (supp) { | |
13adc57b AQ |
762 | peer->authentication_status |= IBSS_RSN_AUTH_EAPOL_BY_PEER; |
763 | wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant from " | |
764 | MACSTR, MAC2STR(peer->addr)); | |
8be18440 JM |
765 | wpa_sm_rx_eapol(peer->supp, peer->addr, tmp, len); |
766 | } else { | |
13adc57b AQ |
767 | if (ibss_rsn_is_auth_started(peer) == 0) { |
768 | wpa_printf(MSG_DEBUG, "RSN: IBSS EAPOL for " | |
769 | "Authenticator dropped as " MACSTR " is not " | |
770 | "authenticated", MAC2STR(peer->addr)); | |
771 | os_free(tmp); | |
772 | return -1; | |
773 | } | |
774 | ||
775 | wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Authenticator " | |
776 | "from "MACSTR, MAC2STR(peer->addr)); | |
8be18440 JM |
777 | wpa_receive(ibss_rsn->auth_group, peer->auth, tmp, len); |
778 | } | |
779 | os_free(tmp); | |
780 | ||
781 | return 1; | |
782 | } | |
783 | ||
784 | ||
785 | int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr, | |
786 | const u8 *buf, size_t len) | |
787 | { | |
788 | struct ibss_rsn_peer *peer; | |
789 | ||
78177a00 JM |
790 | if (ibss_rsn == NULL) |
791 | return -1; | |
792 | ||
fa7187bf AQ |
793 | peer = ibss_rsn_get_peer(ibss_rsn, src_addr); |
794 | if (peer) | |
795 | return ibss_rsn_process_rx_eapol(ibss_rsn, peer, buf, len); | |
8be18440 JM |
796 | |
797 | if (ibss_rsn_eapol_dst_supp(buf, len) > 0) { | |
798 | /* | |
799 | * Create new IBSS peer based on an EAPOL message from the peer | |
800 | * Authenticator. | |
801 | */ | |
13adc57b AQ |
802 | peer = ibss_rsn_peer_init(ibss_rsn, src_addr); |
803 | if (peer == NULL) | |
8be18440 | 804 | return -1; |
13adc57b AQ |
805 | |
806 | /* assume the peer is authenticated already */ | |
807 | wpa_printf(MSG_DEBUG, "RSN: IBSS Not using IBSS Auth for peer " | |
808 | MACSTR, MAC2STR(src_addr)); | |
809 | ibss_rsn_peer_authenticated(ibss_rsn, peer, | |
810 | IBSS_RSN_AUTH_EAPOL_BY_US); | |
811 | ||
8be18440 JM |
812 | return ibss_rsn_process_rx_eapol(ibss_rsn, ibss_rsn->peers, |
813 | buf, len); | |
814 | } | |
815 | ||
816 | return 0; | |
817 | } | |
b9a2e577 | 818 | |
b9a2e577 JM |
819 | void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk) |
820 | { | |
78177a00 JM |
821 | if (ibss_rsn == NULL) |
822 | return; | |
b9a2e577 JM |
823 | os_memcpy(ibss_rsn->psk, psk, PMK_LEN); |
824 | } | |
13adc57b AQ |
825 | |
826 | ||
827 | static void ibss_rsn_handle_auth_1_of_2(struct ibss_rsn *ibss_rsn, | |
828 | struct ibss_rsn_peer *peer, | |
829 | const u8* addr) | |
830 | { | |
831 | wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 1) from " MACSTR, | |
832 | MAC2STR(addr)); | |
833 | ||
834 | if (peer && | |
835 | peer->authentication_status & IBSS_RSN_AUTH_EAPOL_BY_PEER) { | |
6fb7b58f | 836 | if (peer->own_auth_tx.sec) { |
3326f193 JB |
837 | struct os_reltime now, diff; |
838 | os_get_reltime(&now); | |
839 | os_reltime_sub(&now, &peer->own_auth_tx, &diff); | |
6fb7b58f JM |
840 | if (diff.sec == 0 && diff.usec < 500000) { |
841 | wpa_printf(MSG_DEBUG, "RSN: Skip IBSS reinit since only %u usec from own Auth frame TX", | |
842 | (int) diff.usec); | |
843 | goto skip_reinit; | |
844 | } | |
845 | } | |
13adc57b AQ |
846 | /* |
847 | * A peer sent us an Authentication frame even though it already | |
848 | * started an EAPOL session. We should reinit state machines | |
849 | * here, but it's much more complicated than just deleting and | |
850 | * recreating the state machine | |
851 | */ | |
852 | wpa_printf(MSG_DEBUG, "RSN: IBSS Reinitializing station " | |
853 | MACSTR, MAC2STR(addr)); | |
854 | ||
855 | ibss_rsn_stop(ibss_rsn, addr); | |
856 | peer = NULL; | |
857 | } | |
858 | ||
859 | if (!peer) { | |
860 | peer = ibss_rsn_peer_init(ibss_rsn, addr); | |
861 | if (!peer) | |
862 | return; | |
863 | ||
864 | wpa_printf(MSG_DEBUG, "RSN: IBSS Auth started by peer " MACSTR, | |
865 | MAC2STR(addr)); | |
866 | } | |
867 | ||
6fb7b58f | 868 | skip_reinit: |
13adc57b AQ |
869 | /* reply with an Authentication frame now, before sending an EAPOL */ |
870 | ibss_rsn_send_auth(ibss_rsn, addr, 2); | |
871 | /* no need to start another AUTH challenge in the other way.. */ | |
872 | ibss_rsn_peer_authenticated(ibss_rsn, peer, IBSS_RSN_AUTH_EAPOL_BY_US); | |
873 | } | |
874 | ||
875 | ||
876 | void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame, | |
877 | size_t len) | |
878 | { | |
879 | const struct ieee80211_mgmt *header; | |
880 | struct ibss_rsn_peer *peer; | |
881 | size_t auth_length; | |
882 | ||
883 | header = (const struct ieee80211_mgmt *) auth_frame; | |
884 | auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth); | |
885 | ||
886 | if (ibss_rsn == NULL || len < auth_length) | |
887 | return; | |
888 | ||
889 | if (le_to_host16(header->u.auth.auth_alg) != WLAN_AUTH_OPEN || | |
890 | le_to_host16(header->u.auth.status_code) != WLAN_STATUS_SUCCESS) | |
891 | return; | |
892 | ||
893 | peer = ibss_rsn_get_peer(ibss_rsn, header->sa); | |
894 | ||
895 | switch (le_to_host16(header->u.auth.auth_transaction)) { | |
896 | case 1: | |
897 | ibss_rsn_handle_auth_1_of_2(ibss_rsn, peer, header->sa); | |
898 | break; | |
899 | case 2: | |
900 | wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 2) from " | |
901 | MACSTR, MAC2STR(header->sa)); | |
902 | if (!peer) { | |
903 | wpa_printf(MSG_DEBUG, "RSN: Received Auth seq 2 from " | |
904 | "unknown STA " MACSTR, MAC2STR(header->sa)); | |
905 | break; | |
906 | } | |
907 | ||
908 | /* authentication has been completed */ | |
add7add0 JM |
909 | eloop_cancel_timeout(ibss_rsn_auth_timeout, peer, NULL); |
910 | wpa_printf(MSG_DEBUG, "RSN: IBSS Auth completed with " MACSTR, | |
13adc57b AQ |
911 | MAC2STR(header->sa)); |
912 | ibss_rsn_peer_authenticated(ibss_rsn, peer, | |
913 | IBSS_RSN_AUTH_BY_US); | |
914 | break; | |
915 | } | |
916 | } |