]>
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"); | |
b97a5410 | 224 | os_free(ctx); |
11ef8d35 JM |
225 | return -1; |
226 | } | |
227 | ||
228 | wpa_sm_set_own_addr(peer->supp, own_addr); | |
229 | wpa_sm_set_param(peer->supp, WPA_PARAM_RSN_ENABLED, 1); | |
230 | wpa_sm_set_param(peer->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN); | |
231 | wpa_sm_set_param(peer->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP); | |
232 | wpa_sm_set_param(peer->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP); | |
233 | wpa_sm_set_param(peer->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK); | |
70c93963 | 234 | wpa_sm_set_pmk(peer->supp, psk, PMK_LEN, NULL, NULL); |
11ef8d35 | 235 | |
11ef8d35 JM |
236 | peer->supp_ie_len = sizeof(peer->supp_ie); |
237 | if (wpa_sm_set_assoc_wpa_ie_default(peer->supp, peer->supp_ie, | |
238 | &peer->supp_ie_len) < 0) { | |
239 | wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()" | |
240 | " failed"); | |
241 | return -1; | |
242 | } | |
11ef8d35 JM |
243 | |
244 | wpa_sm_notify_assoc(peer->supp, peer->addr); | |
245 | ||
246 | return 0; | |
247 | } | |
248 | ||
249 | ||
250 | static void auth_logger(void *ctx, const u8 *addr, logger_level level, | |
251 | const char *txt) | |
252 | { | |
253 | if (addr) | |
254 | wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s", | |
255 | MAC2STR(addr), txt); | |
256 | else | |
257 | wpa_printf(MSG_DEBUG, "AUTH: %s", txt); | |
258 | } | |
259 | ||
260 | ||
759fd76b JM |
261 | static const u8 * auth_get_psk(void *ctx, const u8 *addr, |
262 | const u8 *p2p_dev_addr, const u8 *prev_psk) | |
11ef8d35 JM |
263 | { |
264 | struct ibss_rsn *ibss_rsn = ctx; | |
265 | wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", | |
266 | __func__, MAC2STR(addr), prev_psk); | |
267 | if (prev_psk) | |
268 | return NULL; | |
269 | return ibss_rsn->psk; | |
270 | } | |
271 | ||
272 | ||
273 | static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data, | |
274 | size_t data_len, int encrypt) | |
275 | { | |
da3c6a5e JM |
276 | struct ibss_rsn *ibss_rsn = ctx; |
277 | struct wpa_supplicant *wpa_s = ibss_rsn->wpa_s; | |
11ef8d35 JM |
278 | |
279 | wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu " | |
280 | "encrypt=%d)", | |
281 | __func__, MAC2STR(addr), (unsigned long) data_len, encrypt); | |
282 | ||
e2d492dd JM |
283 | if (wpa_s->l2) |
284 | return l2_packet_send(wpa_s->l2, addr, ETH_P_EAPOL, data, | |
285 | data_len); | |
11ef8d35 | 286 | |
2961bfa8 | 287 | return -1; |
11ef8d35 JM |
288 | } |
289 | ||
290 | ||
71934751 JM |
291 | static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, |
292 | const u8 *addr, int idx, u8 *key, size_t key_len) | |
98f14041 JM |
293 | { |
294 | struct ibss_rsn *ibss_rsn = ctx; | |
295 | u8 seq[6]; | |
98f14041 JM |
296 | |
297 | os_memset(seq, 0, sizeof(seq)); | |
a486c0c7 JM |
298 | |
299 | if (addr) { | |
300 | wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d addr=" MACSTR | |
301 | " key_idx=%d)", | |
302 | __func__, alg, MAC2STR(addr), idx); | |
303 | } else { | |
304 | wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d key_idx=%d)", | |
305 | __func__, alg, idx); | |
306 | } | |
307 | wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len); | |
308 | ||
98f14041 | 309 | if (idx == 0) { |
e743db43 JM |
310 | if (addr) { |
311 | struct ibss_rsn_peer *peer; | |
312 | peer = ibss_rsn_get_peer(ibss_rsn, addr); | |
313 | if (peer) { | |
314 | peer->authentication_status |= | |
315 | IBSS_RSN_SET_PTK_AUTH; | |
316 | ibss_check_rsn_completed(peer); | |
317 | } | |
318 | } | |
98f14041 JM |
319 | /* |
320 | * In IBSS RSN, the pairwise key from the 4-way handshake | |
321 | * initiated by the peer with highest MAC address is used. | |
322 | */ | |
a416fb47 JM |
323 | if (addr == NULL || |
324 | os_memcmp(ibss_rsn->wpa_s->own_addr, addr, ETH_ALEN) < 0) { | |
a486c0c7 | 325 | wpa_printf(MSG_DEBUG, "AUTH: Do not use this PTK"); |
98f14041 | 326 | return 0; |
a486c0c7 | 327 | } |
98f14041 JM |
328 | } |
329 | ||
330 | return wpa_drv_set_key(ibss_rsn->wpa_s, alg, addr, idx, | |
331 | 1, seq, 6, key, key_len); | |
332 | } | |
333 | ||
334 | ||
1aef400b BB |
335 | static void ibss_rsn_disconnect(void *ctx, const u8 *addr, u16 reason) |
336 | { | |
337 | struct ibss_rsn *ibss_rsn = ctx; | |
338 | wpa_drv_sta_deauth(ibss_rsn->wpa_s, addr, reason); | |
339 | } | |
340 | ||
341 | ||
92ac6376 XC |
342 | static int auth_for_each_sta(void *ctx, int (*cb)(struct wpa_state_machine *sm, |
343 | void *ctx), | |
344 | void *cb_ctx) | |
345 | { | |
346 | struct ibss_rsn *ibss_rsn = ctx; | |
347 | struct ibss_rsn_peer *peer; | |
348 | ||
349 | wpa_printf(MSG_DEBUG, "AUTH: for_each_sta"); | |
350 | ||
351 | for (peer = ibss_rsn->peers; peer; peer = peer->next) { | |
352 | if (peer->auth && cb(peer->auth, cb_ctx)) | |
353 | return 1; | |
354 | } | |
355 | ||
356 | return 0; | |
357 | } | |
358 | ||
359 | ||
e640888c AQ |
360 | static void ibss_set_sta_authorized(struct ibss_rsn *ibss_rsn, |
361 | struct ibss_rsn_peer *peer, int authorized) | |
362 | { | |
363 | int res; | |
364 | ||
365 | if (authorized) { | |
366 | res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr, | |
367 | WPA_STA_AUTHORIZED, | |
368 | WPA_STA_AUTHORIZED, ~0); | |
369 | wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " authorizing port", | |
370 | MAC2STR(peer->addr)); | |
371 | } else { | |
372 | res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr, | |
373 | 0, 0, ~WPA_STA_AUTHORIZED); | |
374 | wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " unauthorizing port", | |
375 | MAC2STR(peer->addr)); | |
376 | } | |
377 | ||
378 | if (res && errno != ENOENT) { | |
379 | wpa_printf(MSG_DEBUG, "Could not set station " MACSTR " flags " | |
380 | "for kernel driver (errno=%d)", | |
381 | MAC2STR(peer->addr), errno); | |
382 | } | |
383 | } | |
384 | ||
385 | ||
386 | static void auth_set_eapol(void *ctx, const u8 *addr, | |
387 | wpa_eapol_variable var, int value) | |
388 | { | |
389 | struct ibss_rsn *ibss_rsn = ctx; | |
390 | struct ibss_rsn_peer *peer = ibss_rsn_get_peer(ibss_rsn, addr); | |
391 | ||
392 | if (peer == NULL) | |
393 | return; | |
394 | ||
395 | switch (var) { | |
396 | case WPA_EAPOL_authorized: | |
397 | ibss_set_sta_authorized(ibss_rsn, peer, value); | |
398 | break; | |
399 | default: | |
400 | /* do not handle any other event */ | |
401 | wpa_printf(MSG_DEBUG, "AUTH: eapol event not handled %d", var); | |
402 | break; | |
403 | } | |
404 | } | |
405 | ||
406 | ||
11ef8d35 | 407 | static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, |
6c33ca9f | 408 | const u8 *own_addr, struct wpa_ssid *ssid) |
11ef8d35 JM |
409 | { |
410 | struct wpa_auth_config conf; | |
cef8fac0 JB |
411 | static const struct wpa_auth_callbacks cb = { |
412 | .logger = auth_logger, | |
413 | .set_eapol = auth_set_eapol, | |
414 | .send_eapol = auth_send_eapol, | |
415 | .get_psk = auth_get_psk, | |
416 | .set_key = auth_set_key, | |
417 | .for_each_sta = auth_for_each_sta, | |
418 | .disconnect = ibss_rsn_disconnect, | |
419 | }; | |
11ef8d35 JM |
420 | |
421 | wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine"); | |
422 | ||
423 | os_memset(&conf, 0, sizeof(conf)); | |
424 | conf.wpa = 2; | |
425 | conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK; | |
426 | conf.wpa_pairwise = WPA_CIPHER_CCMP; | |
427 | conf.rsn_pairwise = WPA_CIPHER_CCMP; | |
428 | conf.wpa_group = WPA_CIPHER_CCMP; | |
429 | conf.eapol_version = 2; | |
6c33ca9f | 430 | conf.wpa_group_rekey = ssid->group_rekey ? ssid->group_rekey : 600; |
11ef8d35 | 431 | |
cef8fac0 | 432 | ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb, ibss_rsn); |
11ef8d35 JM |
433 | if (ibss_rsn->auth_group == NULL) { |
434 | wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed"); | |
435 | return -1; | |
436 | } | |
437 | ||
457a126e JB |
438 | wpa_init_keys(ibss_rsn->auth_group); |
439 | ||
11ef8d35 JM |
440 | return 0; |
441 | } | |
442 | ||
443 | ||
444 | static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn, | |
445 | struct ibss_rsn_peer *peer) | |
446 | { | |
94ddef3e | 447 | peer->auth = wpa_auth_sta_init(ibss_rsn->auth_group, peer->addr, NULL); |
11ef8d35 JM |
448 | if (peer->auth == NULL) { |
449 | wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed"); | |
450 | return -1; | |
451 | } | |
452 | ||
d0fc6e12 JM |
453 | /* TODO: get peer RSN IE with Probe Request */ |
454 | if (wpa_validate_wpa_ie(ibss_rsn->auth_group, peer->auth, | |
4691fc7b | 455 | (u8 *) "\x30\x14\x01\x00" |
d0fc6e12 JM |
456 | "\x00\x0f\xac\x04" |
457 | "\x01\x00\x00\x0f\xac\x04" | |
4691fc7b JM |
458 | "\x01\x00\x00\x0f\xac\x02" |
459 | "\x00\x00", 22, NULL, 0) != | |
d0fc6e12 | 460 | WPA_IE_OK) { |
11ef8d35 JM |
461 | wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed"); |
462 | return -1; | |
463 | } | |
11ef8d35 | 464 | |
6f9b5d16 JM |
465 | if (wpa_auth_sm_event(peer->auth, WPA_ASSOC)) |
466 | return -1; | |
11ef8d35 | 467 | |
6f9b5d16 JM |
468 | if (wpa_auth_sta_associated(ibss_rsn->auth_group, peer->auth)) |
469 | return -1; | |
11ef8d35 JM |
470 | |
471 | return 0; | |
472 | } | |
473 | ||
474 | ||
13adc57b | 475 | static int ibss_rsn_send_auth(struct ibss_rsn *ibss_rsn, const u8 *da, int seq) |
11ef8d35 | 476 | { |
13adc57b AQ |
477 | struct ieee80211_mgmt auth; |
478 | const size_t auth_length = IEEE80211_HDRLEN + sizeof(auth.u.auth); | |
479 | struct wpa_supplicant *wpa_s = ibss_rsn->wpa_s; | |
11ef8d35 | 480 | |
13adc57b | 481 | if (wpa_s->driver->send_frame == NULL) |
78177a00 JM |
482 | return -1; |
483 | ||
13adc57b AQ |
484 | os_memset(&auth, 0, sizeof(auth)); |
485 | ||
486 | auth.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, | |
487 | WLAN_FC_STYPE_AUTH); | |
488 | os_memcpy(auth.da, da, ETH_ALEN); | |
489 | os_memcpy(auth.sa, wpa_s->own_addr, ETH_ALEN); | |
490 | os_memcpy(auth.bssid, wpa_s->bssid, ETH_ALEN); | |
491 | ||
492 | auth.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN); | |
493 | auth.u.auth.auth_transaction = host_to_le16(seq); | |
494 | auth.u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS); | |
495 | ||
496 | wpa_printf(MSG_DEBUG, "RSN: IBSS TX Auth frame (SEQ %d) to " MACSTR, | |
497 | seq, MAC2STR(da)); | |
498 | ||
499 | return wpa_s->driver->send_frame(wpa_s->drv_priv, (u8 *) &auth, | |
500 | auth_length, 0); | |
501 | } | |
502 | ||
503 | ||
504 | static int ibss_rsn_is_auth_started(struct ibss_rsn_peer * peer) | |
505 | { | |
506 | return peer->authentication_status & | |
507 | (IBSS_RSN_AUTH_BY_US | IBSS_RSN_AUTH_EAPOL_BY_US); | |
508 | } | |
509 | ||
510 | ||
511 | static struct ibss_rsn_peer * | |
512 | ibss_rsn_peer_init(struct ibss_rsn *ibss_rsn, const u8 *addr) | |
513 | { | |
514 | struct ibss_rsn_peer *peer; | |
515 | if (ibss_rsn == NULL) | |
516 | return NULL; | |
517 | ||
518 | peer = ibss_rsn_get_peer(ibss_rsn, addr); | |
519 | if (peer) { | |
520 | wpa_printf(MSG_DEBUG, "RSN: IBSS Supplicant for peer "MACSTR | |
521 | " already running", MAC2STR(addr)); | |
522 | return peer; | |
6c9a98a2 JM |
523 | } |
524 | ||
13adc57b AQ |
525 | wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Supplicant for peer "MACSTR, |
526 | MAC2STR(addr)); | |
11ef8d35 JM |
527 | |
528 | peer = os_zalloc(sizeof(*peer)); | |
13adc57b AQ |
529 | if (peer == NULL) { |
530 | wpa_printf(MSG_DEBUG, "RSN: Could not allocate memory."); | |
531 | return NULL; | |
532 | } | |
11ef8d35 | 533 | |
e2d492dd | 534 | peer->ibss_rsn = ibss_rsn; |
11ef8d35 | 535 | os_memcpy(peer->addr, addr, ETH_ALEN); |
13adc57b | 536 | peer->authentication_status = IBSS_RSN_AUTH_NOT_AUTHENTICATED; |
11ef8d35 | 537 | |
13adc57b AQ |
538 | if (ibss_rsn_supp_init(peer, ibss_rsn->wpa_s->own_addr, |
539 | ibss_rsn->psk) < 0) { | |
11ef8d35 | 540 | ibss_rsn_free(peer); |
13adc57b AQ |
541 | return NULL; |
542 | } | |
543 | ||
544 | peer->next = ibss_rsn->peers; | |
545 | ibss_rsn->peers = peer; | |
546 | ||
547 | return peer; | |
548 | } | |
549 | ||
550 | ||
add7add0 JM |
551 | static void ibss_rsn_auth_timeout(void *eloop_ctx, void *timeout_ctx) |
552 | { | |
553 | struct ibss_rsn_peer *peer = eloop_ctx; | |
554 | ||
555 | /* | |
556 | * Assume peer does not support Authentication exchange or the frame was | |
557 | * lost somewhere - start EAPOL Authenticator. | |
558 | */ | |
559 | wpa_printf(MSG_DEBUG, | |
560 | "RSN: Timeout on waiting Authentication frame response from " | |
561 | MACSTR " - start authenticator", MAC2STR(peer->addr)); | |
562 | ||
563 | peer->authentication_status |= IBSS_RSN_AUTH_BY_US; | |
564 | ibss_rsn_auth_init(peer->ibss_rsn, peer); | |
565 | } | |
566 | ||
567 | ||
13adc57b AQ |
568 | int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr) |
569 | { | |
570 | struct ibss_rsn_peer *peer; | |
571 | int res; | |
572 | ||
01e87ef6 EA |
573 | if (!ibss_rsn) |
574 | return -1; | |
575 | ||
13adc57b AQ |
576 | /* if the peer already exists, exit immediately */ |
577 | peer = ibss_rsn_get_peer(ibss_rsn, addr); | |
578 | if (peer) | |
579 | return 0; | |
580 | ||
581 | peer = ibss_rsn_peer_init(ibss_rsn, addr); | |
582 | if (peer == NULL) | |
11ef8d35 | 583 | return -1; |
13adc57b AQ |
584 | |
585 | /* Open Authentication: send first Authentication frame */ | |
586 | res = ibss_rsn_send_auth(ibss_rsn, addr, 1); | |
587 | if (res) { | |
588 | /* | |
589 | * The driver may not support Authentication frame exchange in | |
590 | * IBSS. Ignore authentication and go through EAPOL exchange. | |
591 | */ | |
592 | peer->authentication_status |= IBSS_RSN_AUTH_BY_US; | |
593 | return ibss_rsn_auth_init(ibss_rsn, peer); | |
6fb7b58f | 594 | } else { |
3326f193 | 595 | os_get_reltime(&peer->own_auth_tx); |
add7add0 | 596 | eloop_register_timeout(1, 0, ibss_rsn_auth_timeout, peer, NULL); |
11ef8d35 JM |
597 | } |
598 | ||
13adc57b AQ |
599 | return 0; |
600 | } | |
601 | ||
602 | ||
603 | static int ibss_rsn_peer_authenticated(struct ibss_rsn *ibss_rsn, | |
604 | struct ibss_rsn_peer *peer, int reason) | |
605 | { | |
606 | int already_started; | |
607 | ||
608 | if (ibss_rsn == NULL || peer == NULL) | |
11ef8d35 | 609 | return -1; |
13adc57b AQ |
610 | |
611 | already_started = ibss_rsn_is_auth_started(peer); | |
612 | peer->authentication_status |= reason; | |
613 | ||
614 | if (already_started) { | |
615 | wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator already " | |
616 | "started for peer " MACSTR, MAC2STR(peer->addr)); | |
617 | return 0; | |
11ef8d35 JM |
618 | } |
619 | ||
13adc57b AQ |
620 | wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator " |
621 | "for now-authenticated peer " MACSTR, MAC2STR(peer->addr)); | |
11ef8d35 | 622 | |
13adc57b | 623 | return ibss_rsn_auth_init(ibss_rsn, peer); |
11ef8d35 JM |
624 | } |
625 | ||
626 | ||
ea244d21 XC |
627 | void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac) |
628 | { | |
629 | struct ibss_rsn_peer *peer, *prev; | |
630 | ||
631 | if (ibss_rsn == NULL) | |
632 | return; | |
633 | ||
634 | if (peermac == NULL) { | |
635 | /* remove all peers */ | |
636 | wpa_printf(MSG_DEBUG, "%s: Remove all peers", __func__); | |
637 | peer = ibss_rsn->peers; | |
638 | while (peer) { | |
639 | prev = peer; | |
640 | peer = peer->next; | |
641 | ibss_rsn_free(prev); | |
642 | ibss_rsn->peers = peer; | |
643 | } | |
644 | } else { | |
645 | /* remove specific peer */ | |
646 | wpa_printf(MSG_DEBUG, "%s: Remove specific peer " MACSTR, | |
647 | __func__, MAC2STR(peermac)); | |
648 | ||
649 | for (prev = NULL, peer = ibss_rsn->peers; peer != NULL; | |
650 | prev = peer, peer = peer->next) { | |
651 | if (os_memcmp(peermac, peer->addr, ETH_ALEN) == 0) { | |
652 | if (prev == NULL) | |
653 | ibss_rsn->peers = peer->next; | |
654 | else | |
655 | prev->next = peer->next; | |
656 | ibss_rsn_free(peer); | |
657 | wpa_printf(MSG_DEBUG, "%s: Successfully " | |
658 | "removed a specific peer", | |
659 | __func__); | |
660 | break; | |
661 | } | |
662 | } | |
663 | } | |
664 | } | |
665 | ||
666 | ||
6c33ca9f JM |
667 | struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s, |
668 | struct wpa_ssid *ssid) | |
11ef8d35 JM |
669 | { |
670 | struct ibss_rsn *ibss_rsn; | |
671 | ||
672 | ibss_rsn = os_zalloc(sizeof(*ibss_rsn)); | |
673 | if (ibss_rsn == NULL) | |
674 | return NULL; | |
675 | ibss_rsn->wpa_s = wpa_s; | |
676 | ||
6c33ca9f | 677 | if (ibss_rsn_auth_init_group(ibss_rsn, wpa_s->own_addr, ssid) < 0) { |
11ef8d35 JM |
678 | ibss_rsn_deinit(ibss_rsn); |
679 | return NULL; | |
680 | } | |
681 | ||
682 | return ibss_rsn; | |
683 | } | |
684 | ||
685 | ||
686 | void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn) | |
687 | { | |
688 | struct ibss_rsn_peer *peer, *prev; | |
689 | ||
690 | if (ibss_rsn == NULL) | |
691 | return; | |
692 | ||
693 | peer = ibss_rsn->peers; | |
694 | while (peer) { | |
695 | prev = peer; | |
696 | peer = peer->next; | |
697 | ibss_rsn_free(prev); | |
698 | } | |
699 | ||
6f416c78 JM |
700 | if (ibss_rsn->auth_group) |
701 | wpa_deinit(ibss_rsn->auth_group); | |
11ef8d35 JM |
702 | os_free(ibss_rsn); |
703 | ||
704 | } | |
8be18440 JM |
705 | |
706 | ||
707 | static int ibss_rsn_eapol_dst_supp(const u8 *buf, size_t len) | |
708 | { | |
709 | const struct ieee802_1x_hdr *hdr; | |
710 | const struct wpa_eapol_key *key; | |
711 | u16 key_info; | |
712 | size_t plen; | |
713 | ||
714 | /* TODO: Support other EAPOL packets than just EAPOL-Key */ | |
715 | ||
716 | if (len < sizeof(*hdr) + sizeof(*key)) | |
717 | return -1; | |
718 | ||
719 | hdr = (const struct ieee802_1x_hdr *) buf; | |
720 | key = (const struct wpa_eapol_key *) (hdr + 1); | |
721 | plen = be_to_host16(hdr->length); | |
722 | ||
723 | if (hdr->version < EAPOL_VERSION) { | |
724 | /* TODO: backwards compatibility */ | |
725 | } | |
726 | if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) { | |
727 | wpa_printf(MSG_DEBUG, "RSN: EAPOL frame (type %u) discarded, " | |
728 | "not a Key frame", hdr->type); | |
729 | return -1; | |
730 | } | |
731 | if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) { | |
732 | wpa_printf(MSG_DEBUG, "RSN: EAPOL frame payload size %lu " | |
733 | "invalid (frame size %lu)", | |
734 | (unsigned long) plen, (unsigned long) len); | |
735 | return -1; | |
736 | } | |
737 | ||
738 | if (key->type != EAPOL_KEY_TYPE_RSN) { | |
739 | wpa_printf(MSG_DEBUG, "RSN: EAPOL-Key type (%d) unknown, " | |
740 | "discarded", key->type); | |
741 | return -1; | |
742 | } | |
743 | ||
744 | key_info = WPA_GET_BE16(key->key_info); | |
745 | ||
746 | return !!(key_info & WPA_KEY_INFO_ACK); | |
747 | } | |
748 | ||
749 | ||
750 | static int ibss_rsn_process_rx_eapol(struct ibss_rsn *ibss_rsn, | |
751 | struct ibss_rsn_peer *peer, | |
752 | const u8 *buf, size_t len) | |
753 | { | |
754 | int supp; | |
755 | u8 *tmp; | |
756 | ||
757 | supp = ibss_rsn_eapol_dst_supp(buf, len); | |
758 | if (supp < 0) | |
759 | return -1; | |
760 | ||
761 | tmp = os_malloc(len); | |
762 | if (tmp == NULL) | |
763 | return -1; | |
764 | os_memcpy(tmp, buf, len); | |
765 | if (supp) { | |
13adc57b AQ |
766 | peer->authentication_status |= IBSS_RSN_AUTH_EAPOL_BY_PEER; |
767 | wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant from " | |
768 | MACSTR, MAC2STR(peer->addr)); | |
8be18440 JM |
769 | wpa_sm_rx_eapol(peer->supp, peer->addr, tmp, len); |
770 | } else { | |
13adc57b AQ |
771 | if (ibss_rsn_is_auth_started(peer) == 0) { |
772 | wpa_printf(MSG_DEBUG, "RSN: IBSS EAPOL for " | |
773 | "Authenticator dropped as " MACSTR " is not " | |
774 | "authenticated", MAC2STR(peer->addr)); | |
775 | os_free(tmp); | |
776 | return -1; | |
777 | } | |
778 | ||
779 | wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Authenticator " | |
780 | "from "MACSTR, MAC2STR(peer->addr)); | |
8be18440 JM |
781 | wpa_receive(ibss_rsn->auth_group, peer->auth, tmp, len); |
782 | } | |
783 | os_free(tmp); | |
784 | ||
785 | return 1; | |
786 | } | |
787 | ||
788 | ||
789 | int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr, | |
790 | const u8 *buf, size_t len) | |
791 | { | |
792 | struct ibss_rsn_peer *peer; | |
793 | ||
78177a00 JM |
794 | if (ibss_rsn == NULL) |
795 | return -1; | |
796 | ||
fa7187bf AQ |
797 | peer = ibss_rsn_get_peer(ibss_rsn, src_addr); |
798 | if (peer) | |
799 | return ibss_rsn_process_rx_eapol(ibss_rsn, peer, buf, len); | |
8be18440 JM |
800 | |
801 | if (ibss_rsn_eapol_dst_supp(buf, len) > 0) { | |
802 | /* | |
803 | * Create new IBSS peer based on an EAPOL message from the peer | |
804 | * Authenticator. | |
805 | */ | |
13adc57b AQ |
806 | peer = ibss_rsn_peer_init(ibss_rsn, src_addr); |
807 | if (peer == NULL) | |
8be18440 | 808 | return -1; |
13adc57b AQ |
809 | |
810 | /* assume the peer is authenticated already */ | |
811 | wpa_printf(MSG_DEBUG, "RSN: IBSS Not using IBSS Auth for peer " | |
812 | MACSTR, MAC2STR(src_addr)); | |
813 | ibss_rsn_peer_authenticated(ibss_rsn, peer, | |
814 | IBSS_RSN_AUTH_EAPOL_BY_US); | |
815 | ||
8be18440 JM |
816 | return ibss_rsn_process_rx_eapol(ibss_rsn, ibss_rsn->peers, |
817 | buf, len); | |
818 | } | |
819 | ||
820 | return 0; | |
821 | } | |
b9a2e577 | 822 | |
b9a2e577 JM |
823 | void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk) |
824 | { | |
78177a00 JM |
825 | if (ibss_rsn == NULL) |
826 | return; | |
b9a2e577 JM |
827 | os_memcpy(ibss_rsn->psk, psk, PMK_LEN); |
828 | } | |
13adc57b AQ |
829 | |
830 | ||
831 | static void ibss_rsn_handle_auth_1_of_2(struct ibss_rsn *ibss_rsn, | |
832 | struct ibss_rsn_peer *peer, | |
833 | const u8* addr) | |
834 | { | |
835 | wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 1) from " MACSTR, | |
836 | MAC2STR(addr)); | |
837 | ||
b98706c1 JM |
838 | if (peer && |
839 | peer->authentication_status & (IBSS_RSN_SET_PTK_SUPP | | |
840 | IBSS_RSN_SET_PTK_AUTH)) { | |
841 | /* Clear the TK for this pair to allow recovery from the case | |
842 | * where the peer STA has restarted and lost its key while we | |
843 | * still have a pairwise key configured. */ | |
844 | wpa_printf(MSG_DEBUG, "RSN: Clear pairwise key for peer " | |
845 | MACSTR, MAC2STR(addr)); | |
846 | wpa_drv_set_key(ibss_rsn->wpa_s, WPA_ALG_NONE, addr, 0, 0, | |
847 | NULL, 0, NULL, 0); | |
848 | } | |
849 | ||
13adc57b AQ |
850 | if (peer && |
851 | peer->authentication_status & IBSS_RSN_AUTH_EAPOL_BY_PEER) { | |
6fb7b58f | 852 | if (peer->own_auth_tx.sec) { |
3326f193 JB |
853 | struct os_reltime now, diff; |
854 | os_get_reltime(&now); | |
855 | os_reltime_sub(&now, &peer->own_auth_tx, &diff); | |
6fb7b58f JM |
856 | if (diff.sec == 0 && diff.usec < 500000) { |
857 | wpa_printf(MSG_DEBUG, "RSN: Skip IBSS reinit since only %u usec from own Auth frame TX", | |
858 | (int) diff.usec); | |
859 | goto skip_reinit; | |
860 | } | |
861 | } | |
13adc57b AQ |
862 | /* |
863 | * A peer sent us an Authentication frame even though it already | |
864 | * started an EAPOL session. We should reinit state machines | |
865 | * here, but it's much more complicated than just deleting and | |
866 | * recreating the state machine | |
867 | */ | |
868 | wpa_printf(MSG_DEBUG, "RSN: IBSS Reinitializing station " | |
869 | MACSTR, MAC2STR(addr)); | |
870 | ||
871 | ibss_rsn_stop(ibss_rsn, addr); | |
872 | peer = NULL; | |
873 | } | |
874 | ||
875 | if (!peer) { | |
876 | peer = ibss_rsn_peer_init(ibss_rsn, addr); | |
877 | if (!peer) | |
878 | return; | |
879 | ||
880 | wpa_printf(MSG_DEBUG, "RSN: IBSS Auth started by peer " MACSTR, | |
881 | MAC2STR(addr)); | |
882 | } | |
883 | ||
6fb7b58f | 884 | skip_reinit: |
13adc57b AQ |
885 | /* reply with an Authentication frame now, before sending an EAPOL */ |
886 | ibss_rsn_send_auth(ibss_rsn, addr, 2); | |
887 | /* no need to start another AUTH challenge in the other way.. */ | |
888 | ibss_rsn_peer_authenticated(ibss_rsn, peer, IBSS_RSN_AUTH_EAPOL_BY_US); | |
889 | } | |
890 | ||
891 | ||
892 | void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame, | |
893 | size_t len) | |
894 | { | |
895 | const struct ieee80211_mgmt *header; | |
896 | struct ibss_rsn_peer *peer; | |
897 | size_t auth_length; | |
898 | ||
899 | header = (const struct ieee80211_mgmt *) auth_frame; | |
900 | auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth); | |
901 | ||
902 | if (ibss_rsn == NULL || len < auth_length) | |
903 | return; | |
904 | ||
905 | if (le_to_host16(header->u.auth.auth_alg) != WLAN_AUTH_OPEN || | |
906 | le_to_host16(header->u.auth.status_code) != WLAN_STATUS_SUCCESS) | |
907 | return; | |
908 | ||
909 | peer = ibss_rsn_get_peer(ibss_rsn, header->sa); | |
910 | ||
911 | switch (le_to_host16(header->u.auth.auth_transaction)) { | |
912 | case 1: | |
913 | ibss_rsn_handle_auth_1_of_2(ibss_rsn, peer, header->sa); | |
914 | break; | |
915 | case 2: | |
916 | wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 2) from " | |
917 | MACSTR, MAC2STR(header->sa)); | |
918 | if (!peer) { | |
919 | wpa_printf(MSG_DEBUG, "RSN: Received Auth seq 2 from " | |
920 | "unknown STA " MACSTR, MAC2STR(header->sa)); | |
921 | break; | |
922 | } | |
923 | ||
924 | /* authentication has been completed */ | |
add7add0 JM |
925 | eloop_cancel_timeout(ibss_rsn_auth_timeout, peer, NULL); |
926 | wpa_printf(MSG_DEBUG, "RSN: IBSS Auth completed with " MACSTR, | |
13adc57b AQ |
927 | MAC2STR(header->sa)); |
928 | ibss_rsn_peer_authenticated(ibss_rsn, peer, | |
929 | IBSS_RSN_AUTH_BY_US); | |
930 | break; | |
931 | } | |
932 | } |