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