]>
Commit | Line | Data |
---|---|---|
be27e185 JM |
1 | /* |
2 | * wpa_supplicant - DPP | |
3 | * Copyright (c) 2017, Qualcomm Atheros, Inc. | |
4 | * | |
5 | * This software may be distributed under the terms of the BSD license. | |
6 | * See README for more details. | |
7 | */ | |
8 | ||
9 | #include "utils/includes.h" | |
10 | ||
11 | #include "utils/common.h" | |
30d27b04 | 12 | #include "utils/eloop.h" |
be27e185 JM |
13 | #include "common/dpp.h" |
14 | #include "wpa_supplicant_i.h" | |
30d27b04 JM |
15 | #include "driver_i.h" |
16 | #include "offchannel.h" | |
be27e185 JM |
17 | #include "dpp_supplicant.h" |
18 | ||
19 | ||
30d27b04 JM |
20 | static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s, |
21 | unsigned int freq); | |
22 | static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s, | |
23 | unsigned int freq, const u8 *dst, | |
24 | const u8 *src, const u8 *bssid, | |
25 | const u8 *data, size_t data_len, | |
26 | enum offchannel_send_action_result result); | |
27 | ||
28 | static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | |
29 | ||
30 | ||
be27e185 JM |
31 | static unsigned int wpas_dpp_next_id(struct wpa_supplicant *wpa_s) |
32 | { | |
33 | struct dpp_bootstrap_info *bi; | |
34 | unsigned int max_id = 0; | |
35 | ||
36 | dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info, | |
37 | list) { | |
38 | if (bi->id > max_id) | |
39 | max_id = bi->id; | |
40 | } | |
41 | return max_id + 1; | |
42 | } | |
43 | ||
44 | ||
45 | /** | |
46 | * wpas_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code | |
47 | * @wpa_s: Pointer to wpa_supplicant data | |
48 | * @cmd: DPP URI read from a QR Code | |
49 | * Returns: Identifier of the stored info or -1 on failure | |
50 | */ | |
51 | int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd) | |
52 | { | |
53 | struct dpp_bootstrap_info *bi; | |
30d27b04 | 54 | struct dpp_authentication *auth = wpa_s->dpp_auth; |
be27e185 JM |
55 | |
56 | bi = dpp_parse_qr_code(cmd); | |
57 | if (!bi) | |
58 | return -1; | |
59 | ||
60 | bi->id = wpas_dpp_next_id(wpa_s); | |
61 | dl_list_add(&wpa_s->dpp_bootstrap, &bi->list); | |
62 | ||
30d27b04 JM |
63 | if (auth && auth->response_pending && |
64 | dpp_notify_new_qr_code(auth, bi) == 1) { | |
65 | struct wpabuf *msg; | |
66 | ||
67 | wpa_printf(MSG_DEBUG, | |
68 | "DPP: Sending out pending authentication response"); | |
69 | msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, | |
70 | wpabuf_len(auth->resp_attr)); | |
71 | if (!msg) | |
72 | goto out; | |
73 | wpabuf_put_buf(msg, wpa_s->dpp_auth->resp_attr); | |
74 | ||
75 | offchannel_send_action(wpa_s, auth->curr_freq, | |
76 | auth->peer_mac_addr, wpa_s->own_addr, | |
77 | broadcast, | |
78 | wpabuf_head(msg), wpabuf_len(msg), | |
79 | 500, wpas_dpp_tx_status, 0); | |
80 | wpabuf_free(msg); | |
81 | } | |
82 | ||
83 | out: | |
be27e185 JM |
84 | return bi->id; |
85 | } | |
86 | ||
87 | ||
88 | static char * get_param(const char *cmd, const char *param) | |
89 | { | |
90 | const char *pos, *end; | |
91 | char *val; | |
92 | size_t len; | |
93 | ||
94 | pos = os_strstr(cmd, param); | |
95 | if (!pos) | |
96 | return NULL; | |
97 | ||
98 | pos += os_strlen(param); | |
99 | end = os_strchr(pos, ' '); | |
100 | if (end) | |
101 | len = end - pos; | |
102 | else | |
103 | len = os_strlen(pos); | |
104 | val = os_malloc(len + 1); | |
105 | if (!val) | |
106 | return NULL; | |
107 | os_memcpy(val, pos, len); | |
108 | val[len] = '\0'; | |
109 | return val; | |
110 | } | |
111 | ||
112 | ||
113 | int wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd) | |
114 | { | |
115 | char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL; | |
116 | char *key = NULL; | |
117 | u8 *privkey = NULL; | |
118 | size_t privkey_len = 0; | |
119 | size_t len; | |
120 | int ret = -1; | |
121 | struct dpp_bootstrap_info *bi; | |
122 | ||
123 | bi = os_zalloc(sizeof(*bi)); | |
124 | if (!bi) | |
125 | goto fail; | |
126 | ||
127 | if (os_strstr(cmd, "type=qrcode")) | |
128 | bi->type = DPP_BOOTSTRAP_QR_CODE; | |
129 | else | |
130 | goto fail; | |
131 | ||
132 | chan = get_param(cmd, " chan="); | |
133 | mac = get_param(cmd, " mac="); | |
134 | info = get_param(cmd, " info="); | |
135 | curve = get_param(cmd, " curve="); | |
136 | key = get_param(cmd, " key="); | |
137 | ||
138 | if (key) { | |
139 | privkey_len = os_strlen(key) / 2; | |
140 | privkey = os_malloc(privkey_len); | |
141 | if (!privkey || | |
142 | hexstr2bin(key, privkey, privkey_len) < 0) | |
143 | goto fail; | |
144 | } | |
145 | ||
146 | pk = dpp_keygen(bi, curve, privkey, privkey_len); | |
147 | if (!pk) | |
148 | goto fail; | |
149 | ||
150 | len = 4; /* "DPP:" */ | |
151 | if (chan) { | |
152 | if (dpp_parse_uri_chan_list(bi, chan) < 0) | |
153 | goto fail; | |
154 | len += 3 + os_strlen(chan); /* C:...; */ | |
155 | } | |
156 | if (mac) { | |
157 | if (dpp_parse_uri_mac(bi, mac) < 0) | |
158 | goto fail; | |
159 | len += 3 + os_strlen(mac); /* M:...; */ | |
160 | } | |
161 | if (info) { | |
162 | if (dpp_parse_uri_info(bi, info) < 0) | |
163 | goto fail; | |
164 | len += 3 + os_strlen(info); /* I:...; */ | |
165 | } | |
166 | len += 4 + os_strlen(pk); | |
167 | bi->uri = os_malloc(len + 1); | |
168 | if (!bi->uri) | |
169 | goto fail; | |
170 | os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;", | |
171 | chan ? "C:" : "", chan ? chan : "", chan ? ";" : "", | |
172 | mac ? "M:" : "", mac ? mac : "", mac ? ";" : "", | |
173 | info ? "I:" : "", info ? info : "", info ? ";" : "", | |
174 | pk); | |
175 | bi->id = wpas_dpp_next_id(wpa_s); | |
176 | dl_list_add(&wpa_s->dpp_bootstrap, &bi->list); | |
177 | ret = bi->id; | |
178 | bi = NULL; | |
179 | fail: | |
180 | os_free(curve); | |
181 | os_free(pk); | |
182 | os_free(chan); | |
183 | os_free(mac); | |
184 | os_free(info); | |
185 | str_clear_free(key); | |
186 | bin_clear_free(privkey, privkey_len); | |
187 | dpp_bootstrap_info_free(bi); | |
188 | return ret; | |
189 | } | |
190 | ||
191 | ||
192 | static struct dpp_bootstrap_info * | |
193 | dpp_bootstrap_get_id(struct wpa_supplicant *wpa_s, unsigned int id) | |
194 | { | |
195 | struct dpp_bootstrap_info *bi; | |
196 | ||
197 | dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info, | |
198 | list) { | |
199 | if (bi->id == id) | |
200 | return bi; | |
201 | } | |
202 | return NULL; | |
203 | } | |
204 | ||
205 | ||
206 | static int dpp_bootstrap_del(struct wpa_supplicant *wpa_s, unsigned int id) | |
207 | { | |
208 | struct dpp_bootstrap_info *bi, *tmp; | |
209 | int found = 0; | |
210 | ||
211 | dl_list_for_each_safe(bi, tmp, &wpa_s->dpp_bootstrap, | |
212 | struct dpp_bootstrap_info, list) { | |
213 | if (id && bi->id != id) | |
214 | continue; | |
215 | found = 1; | |
216 | dl_list_del(&bi->list); | |
217 | dpp_bootstrap_info_free(bi); | |
218 | } | |
219 | ||
220 | if (id == 0) | |
221 | return 0; /* flush succeeds regardless of entries found */ | |
222 | return found ? 0 : -1; | |
223 | } | |
224 | ||
225 | ||
226 | int wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, const char *id) | |
227 | { | |
228 | unsigned int id_val; | |
229 | ||
230 | if (os_strcmp(id, "*") == 0) { | |
231 | id_val = 0; | |
232 | } else { | |
233 | id_val = atoi(id); | |
234 | if (id_val == 0) | |
235 | return -1; | |
236 | } | |
237 | ||
238 | return dpp_bootstrap_del(wpa_s, id_val); | |
239 | } | |
240 | ||
241 | ||
242 | const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s, | |
243 | unsigned int id) | |
244 | { | |
245 | struct dpp_bootstrap_info *bi; | |
246 | ||
247 | bi = dpp_bootstrap_get_id(wpa_s, id); | |
248 | if (!bi) | |
249 | return NULL; | |
250 | return bi->uri; | |
251 | } | |
252 | ||
253 | ||
30d27b04 JM |
254 | static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s, |
255 | unsigned int freq, const u8 *dst, | |
256 | const u8 *src, const u8 *bssid, | |
257 | const u8 *data, size_t data_len, | |
258 | enum offchannel_send_action_result result) | |
259 | { | |
260 | wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR | |
261 | " result=%s", | |
262 | freq, MAC2STR(dst), | |
263 | result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" : | |
264 | (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" : | |
265 | "FAILED")); | |
266 | ||
267 | if (!wpa_s->dpp_auth) { | |
268 | wpa_printf(MSG_DEBUG, | |
269 | "DPP: Ignore TX status since there is no ongoing authentication exchange"); | |
270 | return; | |
271 | } | |
272 | ||
273 | if (wpa_s->dpp_auth->remove_on_tx_status) { | |
274 | wpa_printf(MSG_DEBUG, | |
275 | "DPP: Terminate authentication exchange due to an earlier error"); | |
276 | dpp_auth_deinit(wpa_s->dpp_auth); | |
277 | wpa_s->dpp_auth = NULL; | |
278 | return; | |
279 | } | |
280 | ||
281 | if (!is_broadcast_ether_addr(dst) && | |
282 | result != OFFCHANNEL_SEND_ACTION_SUCCESS) { | |
283 | wpa_printf(MSG_DEBUG, | |
284 | "DPP: Unicast DPP Action frame was not ACKed"); | |
285 | /* TODO: In case of DPP Authentication Request frame, move to | |
286 | * the next channel immediately */ | |
287 | } | |
288 | } | |
289 | ||
290 | ||
291 | static void wpas_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx) | |
292 | { | |
293 | struct wpa_supplicant *wpa_s = eloop_ctx; | |
294 | ||
295 | if (!wpa_s->dpp_auth) | |
296 | return; | |
297 | wpa_printf(MSG_DEBUG, "DPP: Continue reply wait on channel %u MHz", | |
298 | wpa_s->dpp_auth->curr_freq); | |
299 | wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->curr_freq); | |
300 | } | |
301 | ||
302 | ||
303 | int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd) | |
304 | { | |
305 | const char *pos; | |
306 | struct dpp_bootstrap_info *peer_bi, *own_bi = NULL; | |
307 | struct wpabuf *msg; | |
308 | const u8 *dst; | |
309 | int res; | |
310 | int configurator = 1; | |
311 | unsigned int wait_time; | |
312 | ||
313 | pos = os_strstr(cmd, " peer="); | |
314 | if (!pos) | |
315 | return -1; | |
316 | pos += 6; | |
317 | peer_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos)); | |
318 | if (!peer_bi) { | |
319 | wpa_printf(MSG_INFO, | |
320 | "DPP: Could not find bootstrapping info for the identified peer"); | |
321 | return -1; | |
322 | } | |
323 | ||
324 | pos = os_strstr(cmd, " own="); | |
325 | if (pos) { | |
326 | pos += 5; | |
327 | own_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos)); | |
328 | if (!own_bi) { | |
329 | wpa_printf(MSG_INFO, | |
330 | "DPP: Could not find bootstrapping info for the identified local entry"); | |
331 | return -1; | |
332 | } | |
333 | ||
334 | if (peer_bi->curve != own_bi->curve) { | |
335 | wpa_printf(MSG_INFO, | |
336 | "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)", | |
337 | peer_bi->curve->name, own_bi->curve->name); | |
338 | return -1; | |
339 | } | |
340 | } | |
341 | ||
342 | pos = os_strstr(cmd, " role="); | |
343 | if (pos) { | |
344 | pos += 6; | |
345 | if (os_strncmp(pos, "configurator", 12) == 0) | |
346 | configurator = 1; | |
347 | else if (os_strncmp(pos, "enrollee", 8) == 0) | |
348 | configurator = 0; | |
349 | else | |
350 | return -1; | |
351 | } | |
352 | ||
353 | if (wpa_s->dpp_auth) { | |
354 | eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); | |
355 | offchannel_send_action_done(wpa_s); | |
356 | dpp_auth_deinit(wpa_s->dpp_auth); | |
357 | } | |
358 | wpa_s->dpp_auth = dpp_auth_init(wpa_s, peer_bi, own_bi, configurator); | |
359 | if (!wpa_s->dpp_auth) | |
360 | return -1; | |
361 | ||
362 | /* TODO: Support iteration over all frequencies and filtering of | |
363 | * frequencies based on locally enabled channels that allow initiation | |
364 | * of transmission. */ | |
365 | if (peer_bi->num_freq > 0) | |
366 | wpa_s->dpp_auth->curr_freq = peer_bi->freq[0]; | |
367 | else | |
368 | wpa_s->dpp_auth->curr_freq = 2412; | |
369 | ||
370 | msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, | |
371 | wpabuf_len(wpa_s->dpp_auth->req_attr)); | |
372 | if (!msg) | |
373 | return -1; | |
374 | wpabuf_put_buf(msg, wpa_s->dpp_auth->req_attr); | |
375 | ||
376 | if (is_zero_ether_addr(peer_bi->mac_addr)) { | |
377 | dst = broadcast; | |
378 | } else { | |
379 | dst = peer_bi->mac_addr; | |
380 | os_memcpy(wpa_s->dpp_auth->peer_mac_addr, peer_bi->mac_addr, | |
381 | ETH_ALEN); | |
382 | } | |
383 | eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); | |
384 | wait_time = wpa_s->max_remain_on_chan; | |
385 | if (wait_time > 2000) | |
386 | wait_time = 2000; | |
387 | eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000, | |
388 | wpas_dpp_reply_wait_timeout, | |
389 | wpa_s, NULL); | |
390 | res = offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq, | |
391 | dst, wpa_s->own_addr, broadcast, | |
392 | wpabuf_head(msg), wpabuf_len(msg), | |
393 | wait_time, wpas_dpp_tx_status, 0); | |
394 | wpabuf_free(msg); | |
395 | ||
396 | return res; | |
397 | } | |
398 | ||
399 | ||
400 | struct wpas_dpp_listen_work { | |
401 | unsigned int freq; | |
402 | unsigned int duration; | |
403 | struct wpabuf *probe_resp_ie; | |
404 | }; | |
405 | ||
406 | ||
407 | static void wpas_dpp_listen_work_free(struct wpas_dpp_listen_work *lwork) | |
408 | { | |
409 | if (!lwork) | |
410 | return; | |
411 | os_free(lwork); | |
412 | } | |
413 | ||
414 | ||
415 | static void wpas_dpp_listen_work_done(struct wpa_supplicant *wpa_s) | |
416 | { | |
417 | struct wpas_dpp_listen_work *lwork; | |
418 | ||
419 | if (!wpa_s->dpp_listen_work) | |
420 | return; | |
421 | ||
422 | lwork = wpa_s->dpp_listen_work->ctx; | |
423 | wpas_dpp_listen_work_free(lwork); | |
424 | radio_work_done(wpa_s->dpp_listen_work); | |
425 | wpa_s->dpp_listen_work = NULL; | |
426 | } | |
427 | ||
428 | ||
429 | static void dpp_start_listen_cb(struct wpa_radio_work *work, int deinit) | |
430 | { | |
431 | struct wpa_supplicant *wpa_s = work->wpa_s; | |
432 | struct wpas_dpp_listen_work *lwork = work->ctx; | |
433 | ||
434 | if (deinit) { | |
435 | if (work->started) { | |
436 | wpa_s->dpp_listen_work = NULL; | |
437 | wpas_dpp_listen_stop(wpa_s); | |
438 | } | |
439 | wpas_dpp_listen_work_free(lwork); | |
440 | return; | |
441 | } | |
442 | ||
443 | wpa_s->dpp_listen_work = work; | |
444 | ||
445 | wpa_s->dpp_pending_listen_freq = lwork->freq; | |
446 | ||
447 | if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, | |
448 | wpa_s->max_remain_on_chan) < 0) { | |
449 | wpa_printf(MSG_DEBUG, | |
450 | "DPP: Failed to request the driver to remain on channel (%u MHz) for listen", | |
451 | lwork->freq); | |
452 | wpas_dpp_listen_work_done(wpa_s); | |
453 | wpa_s->dpp_pending_listen_freq = 0; | |
454 | return; | |
455 | } | |
456 | wpa_s->off_channel_freq = 0; | |
457 | wpa_s->roc_waiting_drv_freq = lwork->freq; | |
458 | } | |
459 | ||
460 | ||
461 | static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s, | |
462 | unsigned int freq) | |
463 | { | |
464 | struct wpas_dpp_listen_work *lwork; | |
465 | ||
466 | if (wpa_s->dpp_listen_work) { | |
467 | wpa_printf(MSG_DEBUG, | |
468 | "DPP: Reject start_listen since dpp_listen_work already exists"); | |
469 | return -1; | |
470 | } | |
471 | ||
472 | if (wpa_s->dpp_listen_freq) | |
473 | wpas_dpp_listen_stop(wpa_s); | |
474 | wpa_s->dpp_listen_freq = freq; | |
475 | ||
476 | lwork = os_zalloc(sizeof(*lwork)); | |
477 | if (!lwork) | |
478 | return -1; | |
479 | lwork->freq = freq; | |
480 | ||
481 | if (radio_add_work(wpa_s, freq, "dpp-listen", 0, dpp_start_listen_cb, | |
482 | lwork) < 0) { | |
483 | wpas_dpp_listen_work_free(lwork); | |
484 | return -1; | |
485 | } | |
486 | ||
487 | return 0; | |
488 | } | |
489 | ||
490 | ||
491 | int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd) | |
492 | { | |
493 | int freq; | |
494 | ||
495 | freq = atoi(cmd); | |
496 | if (freq <= 0) | |
497 | return -1; | |
498 | ||
499 | if (os_strstr(cmd, " role=configurator")) | |
500 | wpa_s->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR; | |
501 | else if (os_strstr(cmd, " role=enrollee")) | |
502 | wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE; | |
503 | else | |
504 | wpa_s->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | | |
505 | DPP_CAPAB_ENROLLEE; | |
506 | wpa_s->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL; | |
507 | if (wpa_s->dpp_listen_freq == (unsigned int) freq) { | |
508 | wpa_printf(MSG_DEBUG, "DPP: Already listening on %u MHz", | |
509 | freq); | |
510 | return 0; | |
511 | } | |
512 | ||
513 | return wpas_dpp_listen_start(wpa_s, freq); | |
514 | } | |
515 | ||
516 | ||
517 | void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s) | |
518 | { | |
519 | if (!wpa_s->dpp_listen_freq) | |
520 | return; | |
521 | ||
522 | wpa_printf(MSG_DEBUG, "DPP: Stop listen on %u MHz", | |
523 | wpa_s->dpp_listen_freq); | |
524 | wpa_drv_cancel_remain_on_channel(wpa_s); | |
525 | wpa_s->dpp_listen_freq = 0; | |
526 | wpas_dpp_listen_work_done(wpa_s); | |
527 | } | |
528 | ||
529 | ||
530 | void wpas_dpp_remain_on_channel_cb(struct wpa_supplicant *wpa_s, | |
531 | unsigned int freq) | |
532 | { | |
533 | if (!wpa_s->dpp_listen_freq && !wpa_s->dpp_pending_listen_freq) | |
534 | return; | |
535 | ||
536 | wpa_printf(MSG_DEBUG, | |
537 | "DPP: remain-on-channel callback (off_channel_freq=%u dpp_pending_listen_freq=%d roc_waiting_drv_freq=%d freq=%u)", | |
538 | wpa_s->off_channel_freq, wpa_s->dpp_pending_listen_freq, | |
539 | wpa_s->roc_waiting_drv_freq, freq); | |
540 | if (wpa_s->off_channel_freq && | |
541 | wpa_s->off_channel_freq == wpa_s->dpp_pending_listen_freq) { | |
542 | wpa_printf(MSG_DEBUG, "DPP: Listen on %u MHz started", freq); | |
543 | wpa_s->dpp_pending_listen_freq = 0; | |
544 | } else { | |
545 | wpa_printf(MSG_DEBUG, | |
546 | "DPP: Ignore remain-on-channel callback (off_channel_freq=%u dpp_pending_listen_freq=%d freq=%u)", | |
547 | wpa_s->off_channel_freq, | |
548 | wpa_s->dpp_pending_listen_freq, freq); | |
549 | } | |
550 | } | |
551 | ||
552 | ||
553 | void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, | |
554 | unsigned int freq) | |
555 | { | |
556 | wpas_dpp_listen_work_done(wpa_s); | |
557 | ||
558 | if (wpa_s->dpp_auth) { | |
559 | /* Continue listen with a new remain-on-channel */ | |
560 | wpa_printf(MSG_DEBUG, | |
561 | "DPP: Continue wait on %u MHz for the ongoing DPP provisioning session", | |
562 | wpa_s->dpp_auth->curr_freq); | |
563 | wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->curr_freq); | |
564 | return; | |
565 | } | |
566 | ||
567 | if (wpa_s->dpp_listen_freq) { | |
568 | /* Continue listen with a new remain-on-channel */ | |
569 | wpas_dpp_listen_start(wpa_s, wpa_s->dpp_listen_freq); | |
570 | } | |
571 | } | |
572 | ||
573 | ||
574 | static void wpas_dpp_rx_auth_req(struct wpa_supplicant *wpa_s, const u8 *src, | |
575 | const u8 *buf, size_t len, unsigned int freq) | |
576 | { | |
577 | const u8 *r_bootstrap, *i_bootstrap, *wrapped_data; | |
578 | u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len; | |
579 | struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL; | |
580 | struct wpabuf *msg; | |
581 | ||
582 | wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR, | |
583 | MAC2STR(src)); | |
584 | ||
585 | wrapped_data = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA, | |
586 | &wrapped_data_len); | |
587 | if (!wrapped_data) { | |
588 | wpa_printf(MSG_DEBUG, | |
589 | "DPP: Missing required Wrapped data attribute"); | |
590 | return; | |
591 | } | |
592 | wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped data", | |
593 | wrapped_data, wrapped_data_len); | |
594 | ||
595 | r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH, | |
596 | &r_bootstrap_len); | |
597 | if (!r_bootstrap || r_bootstrap > wrapped_data || | |
598 | r_bootstrap_len != SHA256_MAC_LEN) { | |
599 | wpa_printf(MSG_DEBUG, | |
600 | "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute"); | |
601 | return; | |
602 | } | |
603 | wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash", | |
604 | r_bootstrap, r_bootstrap_len); | |
605 | ||
606 | i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH, | |
607 | &i_bootstrap_len); | |
608 | if (!i_bootstrap || i_bootstrap > wrapped_data || | |
609 | i_bootstrap_len != SHA256_MAC_LEN) { | |
610 | wpa_printf(MSG_DEBUG, | |
611 | "DPP: Missing or invalid required Initiator Bootstrapping Key Hash attribute"); | |
612 | return; | |
613 | } | |
614 | wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash", | |
615 | i_bootstrap, i_bootstrap_len); | |
616 | ||
617 | /* Try to find own and peer bootstrapping key matches based on the | |
618 | * received hash values */ | |
619 | dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info, | |
620 | list) { | |
621 | if (!own_bi && bi->own && | |
622 | os_memcmp(bi->pubkey_hash, r_bootstrap, | |
623 | SHA256_MAC_LEN) == 0) { | |
624 | wpa_printf(MSG_DEBUG, | |
625 | "DPP: Found matching own bootstrapping information"); | |
626 | own_bi = bi; | |
627 | } | |
628 | ||
629 | if (!peer_bi && !bi->own && | |
630 | os_memcmp(bi->pubkey_hash, i_bootstrap, | |
631 | SHA256_MAC_LEN) == 0) { | |
632 | wpa_printf(MSG_DEBUG, | |
633 | "DPP: Found matching peer bootstrapping information"); | |
634 | peer_bi = bi; | |
635 | } | |
636 | ||
637 | if (own_bi && peer_bi) | |
638 | break; | |
639 | } | |
640 | ||
641 | if (!own_bi) { | |
642 | wpa_printf(MSG_DEBUG, | |
643 | "DPP: No matching own bootstrapping key found - ignore message"); | |
644 | return; | |
645 | } | |
646 | ||
647 | if (wpa_s->dpp_auth) { | |
648 | wpa_printf(MSG_DEBUG, | |
649 | "DPP: Already in DPP authentication exchange - ignore new one"); | |
650 | return; | |
651 | } | |
652 | ||
653 | wpa_s->dpp_auth = dpp_auth_req_rx(wpa_s, wpa_s->dpp_allowed_roles, | |
654 | wpa_s->dpp_qr_mutual, | |
655 | peer_bi, own_bi, freq, buf, | |
656 | wrapped_data, wrapped_data_len); | |
657 | if (!wpa_s->dpp_auth) { | |
658 | wpa_printf(MSG_DEBUG, "DPP: No response generated"); | |
659 | return; | |
660 | } | |
661 | os_memcpy(wpa_s->dpp_auth->peer_mac_addr, src, ETH_ALEN); | |
662 | ||
663 | msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, | |
664 | wpabuf_len(wpa_s->dpp_auth->resp_attr)); | |
665 | if (!msg) | |
666 | return; | |
667 | wpabuf_put_buf(msg, wpa_s->dpp_auth->resp_attr); | |
668 | ||
669 | offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq, | |
670 | src, wpa_s->own_addr, broadcast, | |
671 | wpabuf_head(msg), wpabuf_len(msg), | |
672 | 500, wpas_dpp_tx_status, 0); | |
673 | wpabuf_free(msg); | |
674 | } | |
675 | ||
676 | ||
677 | static void wpas_dpp_rx_auth_resp(struct wpa_supplicant *wpa_s, const u8 *src, | |
678 | const u8 *buf, size_t len) | |
679 | { | |
680 | struct dpp_authentication *auth = wpa_s->dpp_auth; | |
681 | struct wpabuf *msg, *attr; | |
682 | ||
683 | wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR, | |
684 | MAC2STR(src)); | |
685 | ||
686 | if (!auth) { | |
687 | wpa_printf(MSG_DEBUG, | |
688 | "DPP: No DPP Authentication in progress - drop"); | |
689 | return; | |
690 | } | |
691 | ||
692 | if (!is_zero_ether_addr(auth->peer_mac_addr) && | |
693 | os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { | |
694 | wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " | |
695 | MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); | |
696 | return; | |
697 | } | |
698 | ||
699 | eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); | |
700 | ||
701 | attr = dpp_auth_resp_rx(auth, buf, len); | |
702 | if (!attr) { | |
703 | if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) { | |
704 | wpa_printf(MSG_DEBUG, | |
705 | "DPP: Start wait for full response"); | |
706 | offchannel_send_action_done(wpa_s); | |
707 | wpas_dpp_listen_start(wpa_s, auth->curr_freq); | |
708 | return; | |
709 | } | |
710 | wpa_printf(MSG_DEBUG, "DPP: No confirm generated"); | |
711 | return; | |
712 | } | |
713 | os_memcpy(auth->peer_mac_addr, src, ETH_ALEN); | |
714 | ||
715 | msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, wpabuf_len(attr)); | |
716 | if (!msg) { | |
717 | wpabuf_free(attr); | |
718 | return; | |
719 | } | |
720 | wpabuf_put_buf(msg, attr); | |
721 | wpabuf_free(attr); | |
722 | ||
723 | offchannel_send_action(wpa_s, auth->curr_freq, | |
724 | src, wpa_s->own_addr, broadcast, | |
725 | wpabuf_head(msg), wpabuf_len(msg), | |
726 | 500, wpas_dpp_tx_status, 0); | |
727 | wpabuf_free(msg); | |
728 | ||
729 | wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded"); | |
730 | wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=1"); | |
731 | } | |
732 | ||
733 | ||
734 | static void wpas_dpp_rx_auth_conf(struct wpa_supplicant *wpa_s, const u8 *src, | |
735 | const u8 *buf, size_t len) | |
736 | { | |
737 | struct dpp_authentication *auth = wpa_s->dpp_auth; | |
738 | ||
739 | wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR, | |
740 | MAC2STR(src)); | |
741 | ||
742 | if (!auth) { | |
743 | wpa_printf(MSG_DEBUG, | |
744 | "DPP: No DPP Authentication in progress - drop"); | |
745 | return; | |
746 | } | |
747 | ||
748 | if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { | |
749 | wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " | |
750 | MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); | |
751 | return; | |
752 | } | |
753 | ||
754 | if (dpp_auth_conf_rx(auth, buf, len) < 0) { | |
755 | wpa_printf(MSG_DEBUG, "DPP: Authentication failed"); | |
756 | return; | |
757 | } | |
758 | ||
759 | wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded"); | |
760 | wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=0"); | |
761 | } | |
762 | ||
763 | ||
764 | void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, | |
765 | const u8 *buf, size_t len, unsigned int freq) | |
766 | { | |
767 | enum dpp_public_action_frame_type type; | |
768 | ||
769 | if (len < 1) | |
770 | return; | |
771 | type = buf[0]; | |
772 | buf++; | |
773 | len--; | |
774 | ||
775 | wpa_printf(MSG_DEBUG, | |
776 | "DPP: Received DPP Public Action frame type %d from " | |
777 | MACSTR " freq=%u", | |
778 | type, MAC2STR(src), freq); | |
779 | wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len); | |
780 | if (dpp_check_attrs(buf, len) < 0) | |
781 | return; | |
782 | ||
783 | switch (type) { | |
784 | case DPP_PA_AUTHENTICATION_REQ: | |
785 | wpas_dpp_rx_auth_req(wpa_s, src, buf, len, freq); | |
786 | break; | |
787 | case DPP_PA_AUTHENTICATION_RESP: | |
788 | wpas_dpp_rx_auth_resp(wpa_s, src, buf, len); | |
789 | break; | |
790 | case DPP_PA_AUTHENTICATION_CONF: | |
791 | wpas_dpp_rx_auth_conf(wpa_s, src, buf, len); | |
792 | break; | |
793 | default: | |
794 | wpa_printf(MSG_DEBUG, | |
795 | "DPP: Ignored unsupported frame subtype %d", type); | |
796 | break; | |
797 | } | |
798 | } | |
799 | ||
800 | ||
be27e185 JM |
801 | int wpas_dpp_init(struct wpa_supplicant *wpa_s) |
802 | { | |
803 | dl_list_init(&wpa_s->dpp_bootstrap); | |
804 | wpa_s->dpp_init_done = 1; | |
805 | return 0; | |
806 | } | |
807 | ||
808 | ||
809 | void wpas_dpp_deinit(struct wpa_supplicant *wpa_s) | |
810 | { | |
811 | if (!wpa_s->dpp_init_done) | |
812 | return; | |
30d27b04 JM |
813 | eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); |
814 | offchannel_send_action_done(wpa_s); | |
815 | wpas_dpp_listen_stop(wpa_s); | |
be27e185 | 816 | dpp_bootstrap_del(wpa_s, 0); |
30d27b04 JM |
817 | dpp_auth_deinit(wpa_s->dpp_auth); |
818 | wpa_s->dpp_auth = NULL; | |
be27e185 | 819 | } |