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