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