/src/hostap/src/common/dpp_tcp.c
Line | Count | Source |
1 | | /* |
2 | | * DPP over TCP |
3 | | * Copyright (c) 2019-2020, The Linux Foundation |
4 | | * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. |
5 | | * |
6 | | * This software may be distributed under the terms of the BSD license. |
7 | | * See README for more details. |
8 | | */ |
9 | | |
10 | | #include "utils/includes.h" |
11 | | #include <fcntl.h> |
12 | | |
13 | | #include "utils/common.h" |
14 | | #include "utils/ip_addr.h" |
15 | | #include "utils/eloop.h" |
16 | | #include "common/ieee802_11_common.h" |
17 | | #include "common/wpa_ctrl.h" |
18 | | #include "dpp.h" |
19 | | #include "dpp_i.h" |
20 | | |
21 | | #ifdef CONFIG_DPP2 |
22 | | |
23 | | struct dpp_connection { |
24 | | struct dl_list list; |
25 | | struct dpp_controller *ctrl; |
26 | | struct dpp_relay_controller *relay; |
27 | | struct dpp_global *global; |
28 | | struct dpp_pkex *pkex; |
29 | | struct dpp_authentication *auth; |
30 | | void *msg_ctx; |
31 | | void *cb_ctx; |
32 | | int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth); |
33 | | int (*pkex_done)(void *ctx, void *conn, struct dpp_bootstrap_info *bi); |
34 | | bool (*tcp_msg_sent)(void *ctx, struct dpp_authentication *auth); |
35 | | int sock; |
36 | | u8 mac_addr[ETH_ALEN]; |
37 | | unsigned int freq; |
38 | | u8 msg_len[4]; |
39 | | size_t msg_len_octets; |
40 | | struct wpabuf *msg; |
41 | | struct wpabuf *msg_out; |
42 | | size_t msg_out_pos; |
43 | | unsigned int read_eloop:1; |
44 | | unsigned int write_eloop:1; |
45 | | unsigned int on_tcp_tx_complete_gas_done:1; |
46 | | unsigned int on_tcp_tx_complete_remove:1; |
47 | | unsigned int on_tcp_tx_complete_auth_ok:1; |
48 | | unsigned int gas_comeback_in_progress:1; |
49 | | u8 gas_dialog_token; |
50 | | char *name; |
51 | | char *mud_url; |
52 | | char *extra_conf_req_name; |
53 | | char *extra_conf_req_value; |
54 | | enum dpp_netrole netrole; |
55 | | }; |
56 | | |
57 | | /* Remote Controller */ |
58 | | struct dpp_relay_controller { |
59 | | struct dl_list list; |
60 | | struct dpp_global *global; |
61 | | u8 pkhash[SHA256_MAC_LEN]; |
62 | | struct hostapd_ip_addr ipaddr; |
63 | | void *msg_ctx; |
64 | | void *cb_ctx; |
65 | | void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg, |
66 | | size_t len); |
67 | | void (*gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token, |
68 | | int prot, struct wpabuf *buf); |
69 | | struct dl_list conn; /* struct dpp_connection */ |
70 | | }; |
71 | | |
72 | | /* Local Controller */ |
73 | | struct dpp_controller { |
74 | | struct dpp_global *global; |
75 | | u8 allowed_roles; |
76 | | int qr_mutual; |
77 | | int sock; |
78 | | struct dl_list conn; /* struct dpp_connection */ |
79 | | char *configurator_params; |
80 | | enum dpp_netrole netrole; |
81 | | struct dpp_bootstrap_info *pkex_bi; |
82 | | char *pkex_code; |
83 | | char *pkex_identifier; |
84 | | void *msg_ctx; |
85 | | void *cb_ctx; |
86 | | int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth); |
87 | | bool (*tcp_msg_sent)(void *ctx, struct dpp_authentication *auth); |
88 | | }; |
89 | | |
90 | | static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx); |
91 | | static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx); |
92 | | static void dpp_controller_auth_success(struct dpp_connection *conn, |
93 | | int initiator); |
94 | | static void dpp_tcp_build_csr(void *eloop_ctx, void *timeout_ctx); |
95 | | #ifdef CONFIG_DPP3 |
96 | | static void dpp_tcp_build_new_key(void *eloop_ctx, void *timeout_ctx); |
97 | | #endif /* CONFIG_DPP3 */ |
98 | | static void dpp_tcp_gas_query_comeback(void *eloop_ctx, void *timeout_ctx); |
99 | | static void dpp_relay_conn_timeout(void *eloop_ctx, void *timeout_ctx); |
100 | | |
101 | | |
102 | | static void dpp_connection_free(struct dpp_connection *conn) |
103 | 0 | { |
104 | 0 | if (conn->sock >= 0) { |
105 | 0 | wpa_printf(MSG_DEBUG, "DPP: Close Controller socket %d", |
106 | 0 | conn->sock); |
107 | 0 | eloop_unregister_sock(conn->sock, EVENT_TYPE_READ); |
108 | 0 | eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE); |
109 | 0 | close(conn->sock); |
110 | 0 | } |
111 | 0 | eloop_cancel_timeout(dpp_controller_conn_status_result_wait_timeout, |
112 | 0 | conn, NULL); |
113 | 0 | eloop_cancel_timeout(dpp_tcp_build_csr, conn, NULL); |
114 | 0 | eloop_cancel_timeout(dpp_tcp_gas_query_comeback, conn, NULL); |
115 | 0 | eloop_cancel_timeout(dpp_relay_conn_timeout, conn, NULL); |
116 | | #ifdef CONFIG_DPP3 |
117 | | eloop_cancel_timeout(dpp_tcp_build_new_key, conn, NULL); |
118 | | #endif /* CONFIG_DPP3 */ |
119 | 0 | wpabuf_free(conn->msg); |
120 | 0 | wpabuf_free(conn->msg_out); |
121 | 0 | dpp_auth_deinit(conn->auth); |
122 | 0 | dpp_pkex_free(conn->pkex); |
123 | 0 | os_free(conn->name); |
124 | 0 | os_free(conn->mud_url); |
125 | 0 | os_free(conn->extra_conf_req_name); |
126 | 0 | os_free(conn->extra_conf_req_value); |
127 | 0 | os_free(conn); |
128 | 0 | } |
129 | | |
130 | | |
131 | | static void dpp_connection_remove(struct dpp_connection *conn) |
132 | 0 | { |
133 | 0 | dl_list_del(&conn->list); |
134 | 0 | dpp_connection_free(conn); |
135 | 0 | } |
136 | | |
137 | | |
138 | | int dpp_relay_add_controller(struct dpp_global *dpp, |
139 | | struct dpp_relay_config *config) |
140 | 0 | { |
141 | 0 | struct dpp_relay_controller *ctrl; |
142 | 0 | char txt[100]; |
143 | |
|
144 | 0 | if (!dpp) |
145 | 0 | return -1; |
146 | | |
147 | 0 | ctrl = os_zalloc(sizeof(*ctrl)); |
148 | 0 | if (!ctrl) |
149 | 0 | return -1; |
150 | 0 | dl_list_init(&ctrl->conn); |
151 | 0 | ctrl->global = dpp; |
152 | 0 | os_memcpy(&ctrl->ipaddr, config->ipaddr, sizeof(*config->ipaddr)); |
153 | 0 | os_memcpy(ctrl->pkhash, config->pkhash, SHA256_MAC_LEN); |
154 | 0 | ctrl->msg_ctx = config->msg_ctx; |
155 | 0 | ctrl->cb_ctx = config->cb_ctx; |
156 | 0 | ctrl->tx = config->tx; |
157 | 0 | ctrl->gas_resp_tx = config->gas_resp_tx; |
158 | 0 | wpa_printf(MSG_DEBUG, "DPP: Add Relay connection to Controller %s", |
159 | 0 | hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt))); |
160 | 0 | dl_list_add(&dpp->controllers, &ctrl->list); |
161 | 0 | return 0; |
162 | 0 | } |
163 | | |
164 | | |
165 | | static struct dpp_relay_controller * |
166 | | dpp_relay_controller_get(struct dpp_global *dpp, const u8 *pkhash) |
167 | 0 | { |
168 | 0 | struct dpp_relay_controller *ctrl; |
169 | |
|
170 | 0 | if (!dpp) |
171 | 0 | return NULL; |
172 | | |
173 | 0 | dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller, |
174 | 0 | list) { |
175 | 0 | if (os_memcmp(pkhash, ctrl->pkhash, SHA256_MAC_LEN) == 0) |
176 | 0 | return ctrl; |
177 | 0 | } |
178 | | |
179 | 0 | return NULL; |
180 | 0 | } |
181 | | |
182 | | |
183 | | static struct dpp_relay_controller * |
184 | | dpp_relay_controller_get_ctx(struct dpp_global *dpp, void *cb_ctx) |
185 | 0 | { |
186 | 0 | struct dpp_relay_controller *ctrl; |
187 | |
|
188 | 0 | if (!dpp) |
189 | 0 | return NULL; |
190 | | |
191 | 0 | dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller, |
192 | 0 | list) { |
193 | 0 | if (cb_ctx == ctrl->cb_ctx) |
194 | 0 | return ctrl; |
195 | 0 | } |
196 | | |
197 | 0 | return NULL; |
198 | 0 | } |
199 | | |
200 | | |
201 | | static struct dpp_relay_controller * |
202 | | dpp_relay_controller_get_addr(struct dpp_global *dpp, |
203 | | const struct sockaddr_in *addr) |
204 | 0 | { |
205 | 0 | struct dpp_relay_controller *ctrl; |
206 | |
|
207 | 0 | if (!dpp) |
208 | 0 | return NULL; |
209 | | |
210 | 0 | dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller, |
211 | 0 | list) { |
212 | 0 | if (ctrl->ipaddr.af == AF_INET && |
213 | 0 | addr->sin_addr.s_addr == ctrl->ipaddr.u.v4.s_addr) |
214 | 0 | return ctrl; |
215 | 0 | } |
216 | | |
217 | 0 | if (dpp->tmp_controller && |
218 | 0 | dpp->tmp_controller->ipaddr.af == AF_INET && |
219 | 0 | addr->sin_addr.s_addr == dpp->tmp_controller->ipaddr.u.v4.s_addr) |
220 | 0 | return dpp->tmp_controller; |
221 | | |
222 | 0 | return NULL; |
223 | 0 | } |
224 | | |
225 | | |
226 | | static void dpp_controller_gas_done(struct dpp_connection *conn) |
227 | 0 | { |
228 | 0 | struct dpp_authentication *auth = conn->auth; |
229 | |
|
230 | 0 | if (auth->waiting_csr) { |
231 | 0 | wpa_printf(MSG_DEBUG, "DPP: Waiting for CSR"); |
232 | 0 | conn->on_tcp_tx_complete_gas_done = 0; |
233 | 0 | return; |
234 | 0 | } |
235 | | |
236 | | #ifdef CONFIG_DPP3 |
237 | | if (auth->waiting_new_key) { |
238 | | wpa_printf(MSG_DEBUG, "DPP: Waiting for a new key"); |
239 | | conn->on_tcp_tx_complete_gas_done = 0; |
240 | | return; |
241 | | } |
242 | | #endif /* CONFIG_DPP3 */ |
243 | | |
244 | 0 | if (auth->peer_version >= 2 && |
245 | 0 | auth->conf_resp_status == DPP_STATUS_OK) { |
246 | 0 | wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result"); |
247 | 0 | auth->waiting_conf_result = 1; |
248 | 0 | return; |
249 | 0 | } |
250 | | |
251 | 0 | wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT "conf_status=%d", |
252 | 0 | auth->conf_resp_status); |
253 | 0 | dpp_connection_remove(conn); |
254 | 0 | } |
255 | | |
256 | | |
257 | | static int dpp_tcp_send(struct dpp_connection *conn) |
258 | 0 | { |
259 | 0 | int res; |
260 | |
|
261 | 0 | if (!conn->msg_out) { |
262 | 0 | eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE); |
263 | 0 | conn->write_eloop = 0; |
264 | 0 | return -1; |
265 | 0 | } |
266 | 0 | res = send(conn->sock, |
267 | 0 | wpabuf_head_u8(conn->msg_out) + conn->msg_out_pos, |
268 | 0 | wpabuf_len(conn->msg_out) - conn->msg_out_pos, 0); |
269 | 0 | if (res < 0) { |
270 | 0 | wpa_printf(MSG_DEBUG, "DPP: Failed to send buffer: %s", |
271 | 0 | strerror(errno)); |
272 | 0 | dpp_connection_remove(conn); |
273 | 0 | return -1; |
274 | 0 | } |
275 | | |
276 | 0 | conn->msg_out_pos += res; |
277 | 0 | if (wpabuf_len(conn->msg_out) > conn->msg_out_pos) { |
278 | 0 | wpa_printf(MSG_DEBUG, |
279 | 0 | "DPP: %u/%u bytes of message sent to Controller", |
280 | 0 | (unsigned int) conn->msg_out_pos, |
281 | 0 | (unsigned int) wpabuf_len(conn->msg_out)); |
282 | 0 | if (!conn->write_eloop && |
283 | 0 | eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, |
284 | 0 | dpp_conn_tx_ready, conn, NULL) == 0) |
285 | 0 | conn->write_eloop = 1; |
286 | 0 | return 1; |
287 | 0 | } |
288 | | |
289 | 0 | wpa_printf(MSG_DEBUG, "DPP: Full message sent over TCP"); |
290 | 0 | wpabuf_free(conn->msg_out); |
291 | 0 | conn->msg_out = NULL; |
292 | 0 | conn->msg_out_pos = 0; |
293 | 0 | eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE); |
294 | 0 | conn->write_eloop = 0; |
295 | 0 | if (!conn->read_eloop && |
296 | 0 | eloop_register_sock(conn->sock, EVENT_TYPE_READ, |
297 | 0 | dpp_controller_rx, conn, NULL) == 0) |
298 | 0 | conn->read_eloop = 1; |
299 | 0 | if (conn->on_tcp_tx_complete_remove) { |
300 | 0 | if (conn->auth && conn->auth->connect_on_tx_status && |
301 | 0 | conn->tcp_msg_sent && |
302 | 0 | conn->tcp_msg_sent(conn->cb_ctx, conn->auth)) |
303 | 0 | return 0; |
304 | 0 | dpp_connection_remove(conn); |
305 | 0 | } else if (conn->auth && (conn->ctrl || conn->auth->configurator) && |
306 | 0 | conn->on_tcp_tx_complete_gas_done) { |
307 | 0 | dpp_controller_gas_done(conn); |
308 | 0 | } else if (conn->on_tcp_tx_complete_auth_ok) { |
309 | 0 | conn->on_tcp_tx_complete_auth_ok = 0; |
310 | 0 | dpp_controller_auth_success(conn, 1); |
311 | 0 | } |
312 | | |
313 | 0 | return 0; |
314 | 0 | } |
315 | | |
316 | | |
317 | | static int dpp_tcp_send_msg(struct dpp_connection *conn, |
318 | | const struct wpabuf *msg) |
319 | 0 | { |
320 | 0 | wpabuf_free(conn->msg_out); |
321 | 0 | conn->msg_out_pos = 0; |
322 | 0 | conn->msg_out = wpabuf_alloc(4 + wpabuf_len(msg) - 1); |
323 | 0 | if (!conn->msg_out) |
324 | 0 | return -1; |
325 | 0 | wpabuf_put_be32(conn->msg_out, wpabuf_len(msg) - 1); |
326 | 0 | wpabuf_put_data(conn->msg_out, wpabuf_head_u8(msg) + 1, |
327 | 0 | wpabuf_len(msg) - 1); |
328 | |
|
329 | 0 | if (dpp_tcp_send(conn) == 1) { |
330 | 0 | if (!conn->write_eloop) { |
331 | 0 | if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, |
332 | 0 | dpp_conn_tx_ready, |
333 | 0 | conn, NULL) < 0) |
334 | 0 | return -1; |
335 | 0 | conn->write_eloop = 1; |
336 | 0 | } |
337 | 0 | } |
338 | | |
339 | 0 | return 0; |
340 | 0 | } |
341 | | |
342 | | |
343 | | static void dpp_controller_start_gas_client(struct dpp_connection *conn) |
344 | 0 | { |
345 | 0 | struct dpp_authentication *auth = conn->auth; |
346 | 0 | struct wpabuf *buf; |
347 | 0 | const char *dpp_name; |
348 | |
|
349 | 0 | dpp_name = conn->name ? conn->name : "Test"; |
350 | 0 | buf = dpp_build_conf_req_helper(auth, dpp_name, conn->netrole, |
351 | 0 | conn->mud_url, NULL, |
352 | 0 | conn->extra_conf_req_name, |
353 | 0 | conn->extra_conf_req_value); |
354 | 0 | if (!buf) { |
355 | 0 | wpa_printf(MSG_DEBUG, |
356 | 0 | "DPP: No configuration request data available"); |
357 | 0 | return; |
358 | 0 | } |
359 | | |
360 | 0 | dpp_tcp_send_msg(conn, buf); |
361 | 0 | wpabuf_free(buf); |
362 | 0 | } |
363 | | |
364 | | |
365 | | static void dpp_controller_auth_success(struct dpp_connection *conn, |
366 | | int initiator) |
367 | 0 | { |
368 | 0 | struct dpp_authentication *auth = conn->auth; |
369 | |
|
370 | 0 | if (!auth) |
371 | 0 | return; |
372 | | |
373 | 0 | wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded"); |
374 | 0 | dpp_notify_auth_success(auth, initiator); |
375 | | #ifdef CONFIG_TESTING_OPTIONS |
376 | | if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) { |
377 | | wpa_printf(MSG_INFO, |
378 | | "DPP: TESTING - stop at Authentication Confirm"); |
379 | | if (auth->configurator) { |
380 | | /* Prevent GAS response */ |
381 | | auth->auth_success = 0; |
382 | | } |
383 | | return; |
384 | | } |
385 | | #endif /* CONFIG_TESTING_OPTIONS */ |
386 | |
|
387 | 0 | if (!auth->configurator) |
388 | 0 | dpp_controller_start_gas_client(conn); |
389 | 0 | } |
390 | | |
391 | | |
392 | | static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) |
393 | 0 | { |
394 | 0 | struct dpp_connection *conn = eloop_ctx; |
395 | |
|
396 | 0 | wpa_printf(MSG_DEBUG, "DPP: TCP socket %d ready for TX", sock); |
397 | 0 | dpp_tcp_send(conn); |
398 | 0 | } |
399 | | |
400 | | |
401 | | static int dpp_ipaddr_to_sockaddr(struct sockaddr *addr, socklen_t *addrlen, |
402 | | const struct hostapd_ip_addr *ipaddr, |
403 | | int port) |
404 | 0 | { |
405 | 0 | struct sockaddr_in *dst; |
406 | | #ifdef CONFIG_IPV6 |
407 | | struct sockaddr_in6 *dst6; |
408 | | #endif /* CONFIG_IPV6 */ |
409 | |
|
410 | 0 | switch (ipaddr->af) { |
411 | 0 | case AF_INET: |
412 | 0 | dst = (struct sockaddr_in *) addr; |
413 | 0 | os_memset(dst, 0, sizeof(*dst)); |
414 | 0 | dst->sin_family = AF_INET; |
415 | 0 | dst->sin_addr.s_addr = ipaddr->u.v4.s_addr; |
416 | 0 | dst->sin_port = htons(port); |
417 | 0 | *addrlen = sizeof(*dst); |
418 | 0 | break; |
419 | | #ifdef CONFIG_IPV6 |
420 | | case AF_INET6: |
421 | | dst6 = (struct sockaddr_in6 *) addr; |
422 | | os_memset(dst6, 0, sizeof(*dst6)); |
423 | | dst6->sin6_family = AF_INET6; |
424 | | os_memcpy(&dst6->sin6_addr, &ipaddr->u.v6, |
425 | | sizeof(struct in6_addr)); |
426 | | dst6->sin6_port = htons(port); |
427 | | *addrlen = sizeof(*dst6); |
428 | | break; |
429 | | #endif /* CONFIG_IPV6 */ |
430 | 0 | default: |
431 | 0 | return -1; |
432 | 0 | } |
433 | | |
434 | 0 | return 0; |
435 | 0 | } |
436 | | |
437 | | |
438 | | static void dpp_relay_conn_timeout(void *eloop_ctx, void *timeout_ctx) |
439 | 0 | { |
440 | 0 | struct dpp_connection *conn = eloop_ctx; |
441 | |
|
442 | 0 | wpa_printf(MSG_DEBUG, |
443 | 0 | "DPP: Timeout while waiting for relayed connection to complete"); |
444 | 0 | dpp_connection_remove(conn); |
445 | 0 | } |
446 | | |
447 | | |
448 | | static struct dpp_connection * |
449 | | dpp_relay_new_conn(struct dpp_relay_controller *ctrl, const u8 *src, |
450 | | unsigned int freq) |
451 | 0 | { |
452 | 0 | struct dpp_connection *conn; |
453 | 0 | struct sockaddr_storage addr; |
454 | 0 | socklen_t addrlen; |
455 | 0 | char txt[100]; |
456 | |
|
457 | 0 | if (dl_list_len(&ctrl->conn) >= 15) { |
458 | 0 | wpa_printf(MSG_DEBUG, |
459 | 0 | "DPP: Too many ongoing Relay connections to the Controller - cannot start a new one"); |
460 | 0 | return NULL; |
461 | 0 | } |
462 | | |
463 | 0 | if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &addr, &addrlen, |
464 | 0 | &ctrl->ipaddr, DPP_TCP_PORT) < 0) |
465 | 0 | return NULL; |
466 | | |
467 | 0 | conn = os_zalloc(sizeof(*conn)); |
468 | 0 | if (!conn) |
469 | 0 | return NULL; |
470 | | |
471 | 0 | conn->global = ctrl->global; |
472 | 0 | conn->relay = ctrl; |
473 | 0 | conn->msg_ctx = ctrl->msg_ctx; |
474 | 0 | conn->cb_ctx = ctrl->global->cb_ctx; |
475 | 0 | os_memcpy(conn->mac_addr, src, ETH_ALEN); |
476 | 0 | conn->freq = freq; |
477 | |
|
478 | 0 | conn->sock = socket(AF_INET, SOCK_STREAM, 0); |
479 | 0 | if (conn->sock < 0) |
480 | 0 | goto fail; |
481 | 0 | wpa_printf(MSG_DEBUG, "DPP: TCP relay socket %d connection to %s", |
482 | 0 | conn->sock, hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt))); |
483 | |
|
484 | 0 | if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) { |
485 | 0 | wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s", |
486 | 0 | strerror(errno)); |
487 | 0 | goto fail; |
488 | 0 | } |
489 | | |
490 | 0 | if (connect(conn->sock, (struct sockaddr *) &addr, addrlen) < 0) { |
491 | 0 | if (errno != EINPROGRESS) { |
492 | 0 | wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s", |
493 | 0 | strerror(errno)); |
494 | 0 | goto fail; |
495 | 0 | } |
496 | | |
497 | | /* |
498 | | * Continue connecting in the background; eloop will call us |
499 | | * once the connection is ready (or failed). |
500 | | */ |
501 | 0 | } |
502 | | |
503 | 0 | if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, |
504 | 0 | dpp_conn_tx_ready, conn, NULL) < 0) |
505 | 0 | goto fail; |
506 | 0 | conn->write_eloop = 1; |
507 | |
|
508 | 0 | eloop_cancel_timeout(dpp_relay_conn_timeout, conn, NULL); |
509 | 0 | eloop_register_timeout(20, 0, dpp_relay_conn_timeout, conn, NULL); |
510 | |
|
511 | 0 | dl_list_add(&ctrl->conn, &conn->list); |
512 | 0 | return conn; |
513 | 0 | fail: |
514 | 0 | dpp_connection_free(conn); |
515 | 0 | return NULL; |
516 | 0 | } |
517 | | |
518 | | |
519 | | static struct wpabuf * dpp_tcp_encaps(const u8 *hdr, const u8 *buf, size_t len) |
520 | 0 | { |
521 | 0 | struct wpabuf *msg; |
522 | |
|
523 | 0 | msg = wpabuf_alloc(4 + 1 + DPP_HDR_LEN + len); |
524 | 0 | if (!msg) |
525 | 0 | return NULL; |
526 | 0 | wpabuf_put_be32(msg, 1 + DPP_HDR_LEN + len); |
527 | 0 | wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC); |
528 | 0 | wpabuf_put_data(msg, hdr, DPP_HDR_LEN); |
529 | 0 | wpabuf_put_data(msg, buf, len); |
530 | 0 | wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg); |
531 | 0 | return msg; |
532 | 0 | } |
533 | | |
534 | | |
535 | | static int dpp_relay_tx(struct dpp_connection *conn, const u8 *hdr, |
536 | | const u8 *buf, size_t len) |
537 | 0 | { |
538 | 0 | u8 type = hdr[DPP_HDR_LEN - 1]; |
539 | |
|
540 | 0 | wpa_printf(MSG_DEBUG, |
541 | 0 | "DPP: Continue already established Relay/Controller connection for this session"); |
542 | 0 | wpabuf_free(conn->msg_out); |
543 | 0 | conn->msg_out_pos = 0; |
544 | 0 | conn->msg_out = dpp_tcp_encaps(hdr, buf, len); |
545 | 0 | if (!conn->msg_out) { |
546 | 0 | dpp_connection_remove(conn); |
547 | 0 | return -1; |
548 | 0 | } |
549 | | |
550 | | /* TODO: for proto ver 1, need to do remove connection based on GAS Resp |
551 | | * TX status */ |
552 | 0 | if (type == DPP_PA_CONFIGURATION_RESULT) |
553 | 0 | conn->on_tcp_tx_complete_remove = 1; |
554 | 0 | dpp_tcp_send(conn); |
555 | 0 | return 0; |
556 | 0 | } |
557 | | |
558 | | |
559 | | static struct dpp_connection * |
560 | | dpp_relay_match_ctrl(struct dpp_relay_controller *ctrl, const u8 *src, |
561 | | unsigned int freq, u8 type) |
562 | 0 | { |
563 | 0 | struct dpp_connection *conn; |
564 | |
|
565 | 0 | dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) { |
566 | 0 | if (ether_addr_equal(src, conn->mac_addr)) |
567 | 0 | return conn; |
568 | 0 | if ((type == DPP_PA_PKEX_EXCHANGE_RESP || |
569 | 0 | type == DPP_PA_AUTHENTICATION_RESP) && |
570 | 0 | conn->freq == 0 && |
571 | 0 | is_broadcast_ether_addr(conn->mac_addr)) { |
572 | 0 | wpa_printf(MSG_DEBUG, |
573 | 0 | "DPP: Associate this peer to the new Controller initiated connection"); |
574 | 0 | os_memcpy(conn->mac_addr, src, ETH_ALEN); |
575 | 0 | conn->freq = freq; |
576 | 0 | return conn; |
577 | 0 | } |
578 | 0 | } |
579 | | |
580 | 0 | return NULL; |
581 | 0 | } |
582 | | |
583 | | |
584 | | int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr, |
585 | | const u8 *buf, size_t len, unsigned int freq, |
586 | | const u8 *i_bootstrap, const u8 *r_bootstrap, |
587 | | void *cb_ctx) |
588 | 0 | { |
589 | 0 | struct dpp_relay_controller *ctrl; |
590 | 0 | struct dpp_connection *conn; |
591 | 0 | u8 type = hdr[DPP_HDR_LEN - 1]; |
592 | | |
593 | | /* Check if there is an already started session for this peer and if so, |
594 | | * continue that session (send this over TCP) and return 0. |
595 | | */ |
596 | 0 | if (type != DPP_PA_PEER_DISCOVERY_REQ && |
597 | 0 | type != DPP_PA_PEER_DISCOVERY_RESP && |
598 | 0 | type != DPP_PA_PRESENCE_ANNOUNCEMENT && |
599 | 0 | type != DPP_PA_RECONFIG_ANNOUNCEMENT) { |
600 | 0 | dl_list_for_each(ctrl, &dpp->controllers, |
601 | 0 | struct dpp_relay_controller, list) { |
602 | 0 | conn = dpp_relay_match_ctrl(ctrl, src, freq, type); |
603 | 0 | if (conn) |
604 | 0 | return dpp_relay_tx(conn, hdr, buf, len); |
605 | 0 | } |
606 | | |
607 | 0 | if (dpp->tmp_controller) { |
608 | 0 | conn = dpp_relay_match_ctrl(dpp->tmp_controller, src, |
609 | 0 | freq, type); |
610 | 0 | if (conn) |
611 | 0 | return dpp_relay_tx(conn, hdr, buf, len); |
612 | 0 | } |
613 | 0 | } |
614 | | |
615 | 0 | if (type == DPP_PA_PRESENCE_ANNOUNCEMENT || |
616 | 0 | type == DPP_PA_RECONFIG_ANNOUNCEMENT) { |
617 | | /* TODO: Could send this to all configured Controllers. For now, |
618 | | * only the first Controller is supported. */ |
619 | 0 | ctrl = dpp_relay_controller_get_ctx(dpp, cb_ctx); |
620 | 0 | } else if (type == DPP_PA_PKEX_EXCHANGE_REQ) { |
621 | 0 | ctrl = dpp_relay_controller_get_ctx(dpp, cb_ctx); |
622 | 0 | } else { |
623 | 0 | if (!r_bootstrap) |
624 | 0 | return -1; |
625 | 0 | ctrl = dpp_relay_controller_get(dpp, r_bootstrap); |
626 | 0 | } |
627 | 0 | if (!ctrl) |
628 | 0 | return -1; |
629 | | |
630 | 0 | if (type == DPP_PA_PRESENCE_ANNOUNCEMENT || |
631 | 0 | type == DPP_PA_RECONFIG_ANNOUNCEMENT) { |
632 | 0 | conn = dpp_relay_match_ctrl(ctrl, src, freq, type); |
633 | 0 | if (conn && |
634 | 0 | (!conn->auth || conn->auth->waiting_auth_resp)) { |
635 | 0 | wpa_printf(MSG_DEBUG, |
636 | 0 | "DPP: Use existing TCP connection to Controller since no Auth Resp seen on it yet"); |
637 | 0 | return dpp_relay_tx(conn, hdr, buf, len); |
638 | 0 | } |
639 | 0 | } |
640 | | |
641 | 0 | wpa_printf(MSG_DEBUG, |
642 | 0 | "DPP: Authentication Request for a configured Controller"); |
643 | 0 | conn = dpp_relay_new_conn(ctrl, src, freq); |
644 | 0 | if (!conn) |
645 | 0 | return -1; |
646 | | |
647 | 0 | conn->msg_out = dpp_tcp_encaps(hdr, buf, len); |
648 | 0 | if (!conn->msg_out) { |
649 | 0 | dpp_connection_remove(conn); |
650 | 0 | return -1; |
651 | 0 | } |
652 | | /* Message will be sent in dpp_conn_tx_ready() */ |
653 | | |
654 | 0 | return 0; |
655 | 0 | } |
656 | | |
657 | | |
658 | | static struct dpp_connection * |
659 | | dpp_relay_find_conn(struct dpp_relay_controller *ctrl, const u8 *src) |
660 | 0 | { |
661 | 0 | struct dpp_connection *conn; |
662 | |
|
663 | 0 | dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) { |
664 | 0 | if (ether_addr_equal(src, conn->mac_addr)) |
665 | 0 | return conn; |
666 | 0 | } |
667 | | |
668 | 0 | return NULL; |
669 | 0 | } |
670 | | |
671 | | |
672 | | int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data, |
673 | | size_t data_len) |
674 | 0 | { |
675 | 0 | struct dpp_relay_controller *ctrl; |
676 | 0 | struct dpp_connection *conn = NULL; |
677 | 0 | struct wpabuf *msg; |
678 | | |
679 | | /* Check if there is a successfully completed authentication for this |
680 | | * and if so, continue that session (send this over TCP) and return 0. |
681 | | */ |
682 | 0 | dl_list_for_each(ctrl, &dpp->controllers, |
683 | 0 | struct dpp_relay_controller, list) { |
684 | 0 | conn = dpp_relay_find_conn(ctrl, src); |
685 | 0 | if (conn) |
686 | 0 | break; |
687 | 0 | } |
688 | |
|
689 | 0 | if (!conn && dpp->tmp_controller) |
690 | 0 | conn = dpp_relay_find_conn(dpp->tmp_controller, src); |
691 | |
|
692 | 0 | if (!conn) |
693 | 0 | return -1; |
694 | | |
695 | 0 | msg = wpabuf_alloc(4 + 1 + data_len); |
696 | 0 | if (!msg) |
697 | 0 | return -1; |
698 | 0 | wpabuf_put_be32(msg, 1 + data_len); |
699 | 0 | wpabuf_put_u8(msg, WLAN_PA_GAS_INITIAL_REQ); |
700 | 0 | wpabuf_put_data(msg, data, data_len); |
701 | 0 | wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg); |
702 | |
|
703 | 0 | wpabuf_free(conn->msg_out); |
704 | 0 | conn->msg_out_pos = 0; |
705 | 0 | conn->msg_out = msg; |
706 | 0 | dpp_tcp_send(conn); |
707 | 0 | return 0; |
708 | 0 | } |
709 | | |
710 | | |
711 | | bool dpp_relay_controller_available(struct dpp_global *dpp) |
712 | 0 | { |
713 | 0 | return dpp && dl_list_len(&dpp->controllers) > 0; |
714 | 0 | } |
715 | | |
716 | | |
717 | | static void dpp_controller_free(struct dpp_controller *ctrl) |
718 | 1.66k | { |
719 | 1.66k | struct dpp_connection *conn, *tmp; |
720 | | |
721 | 1.66k | if (!ctrl) |
722 | 1.66k | return; |
723 | | |
724 | 0 | dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection, |
725 | 0 | list) |
726 | 0 | dpp_connection_remove(conn); |
727 | |
|
728 | 0 | if (ctrl->sock >= 0) { |
729 | 0 | close(ctrl->sock); |
730 | 0 | eloop_unregister_sock(ctrl->sock, EVENT_TYPE_READ); |
731 | 0 | } |
732 | 0 | os_free(ctrl->configurator_params); |
733 | 0 | os_free(ctrl->pkex_code); |
734 | 0 | os_free(ctrl->pkex_identifier); |
735 | 0 | os_free(ctrl); |
736 | 0 | } |
737 | | |
738 | | |
739 | | static int dpp_controller_rx_auth_req(struct dpp_connection *conn, |
740 | | const u8 *hdr, const u8 *buf, size_t len) |
741 | 0 | { |
742 | 0 | const u8 *r_bootstrap, *i_bootstrap; |
743 | 0 | u16 r_bootstrap_len, i_bootstrap_len; |
744 | 0 | struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL; |
745 | |
|
746 | 0 | if (!conn->ctrl) |
747 | 0 | return 0; |
748 | | |
749 | 0 | wpa_printf(MSG_DEBUG, "DPP: Authentication Request"); |
750 | |
|
751 | 0 | r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH, |
752 | 0 | &r_bootstrap_len); |
753 | 0 | if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { |
754 | 0 | wpa_printf(MSG_INFO, |
755 | 0 | "Missing or invalid required Responder Bootstrapping Key Hash attribute"); |
756 | 0 | return -1; |
757 | 0 | } |
758 | 0 | wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash", |
759 | 0 | r_bootstrap, r_bootstrap_len); |
760 | |
|
761 | 0 | i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH, |
762 | 0 | &i_bootstrap_len); |
763 | 0 | if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) { |
764 | 0 | wpa_printf(MSG_INFO, |
765 | 0 | "Missing or invalid required Initiator Bootstrapping Key Hash attribute"); |
766 | 0 | return -1; |
767 | 0 | } |
768 | 0 | wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash", |
769 | 0 | i_bootstrap, i_bootstrap_len); |
770 | | |
771 | | /* Try to find own and peer bootstrapping key matches based on the |
772 | | * received hash values */ |
773 | 0 | dpp_bootstrap_find_pair(conn->ctrl->global, i_bootstrap, r_bootstrap, |
774 | 0 | &own_bi, &peer_bi); |
775 | 0 | if (!own_bi) { |
776 | 0 | wpa_printf(MSG_INFO, |
777 | 0 | "No matching own bootstrapping key found - ignore message"); |
778 | 0 | return -1; |
779 | 0 | } |
780 | | |
781 | 0 | if (conn->auth) { |
782 | 0 | wpa_printf(MSG_INFO, |
783 | 0 | "Already in DPP authentication exchange - ignore new one"); |
784 | 0 | return 0; |
785 | 0 | } |
786 | | |
787 | 0 | conn->auth = dpp_auth_req_rx(conn->ctrl->global, conn->msg_ctx, |
788 | 0 | conn->ctrl->allowed_roles, |
789 | 0 | conn->ctrl->qr_mutual, |
790 | 0 | peer_bi, own_bi, -1, hdr, buf, len); |
791 | 0 | if (!conn->auth) { |
792 | 0 | wpa_printf(MSG_DEBUG, "DPP: No response generated"); |
793 | 0 | return -1; |
794 | 0 | } |
795 | | |
796 | 0 | if (dpp_set_configurator(conn->auth, |
797 | 0 | conn->ctrl->configurator_params) < 0) |
798 | 0 | return -1; |
799 | | |
800 | 0 | return dpp_tcp_send_msg(conn, conn->auth->resp_msg); |
801 | 0 | } |
802 | | |
803 | | |
804 | | static int dpp_controller_rx_auth_resp(struct dpp_connection *conn, |
805 | | const u8 *hdr, const u8 *buf, size_t len) |
806 | 0 | { |
807 | 0 | struct dpp_authentication *auth = conn->auth; |
808 | 0 | struct wpabuf *msg; |
809 | 0 | int res; |
810 | |
|
811 | 0 | if (!auth) |
812 | 0 | return -1; |
813 | | |
814 | 0 | wpa_printf(MSG_DEBUG, "DPP: Authentication Response"); |
815 | |
|
816 | 0 | msg = dpp_auth_resp_rx(auth, hdr, buf, len); |
817 | 0 | if (!msg) { |
818 | 0 | if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) { |
819 | 0 | wpa_printf(MSG_DEBUG, |
820 | 0 | "DPP: Start wait for full response"); |
821 | 0 | return 0; |
822 | 0 | } |
823 | 0 | wpa_printf(MSG_DEBUG, "DPP: No confirm generated"); |
824 | 0 | return -1; |
825 | 0 | } |
826 | | |
827 | 0 | conn->on_tcp_tx_complete_auth_ok = 1; |
828 | 0 | res = dpp_tcp_send_msg(conn, msg); |
829 | 0 | wpabuf_free(msg); |
830 | 0 | return res; |
831 | 0 | } |
832 | | |
833 | | |
834 | | static int dpp_controller_rx_auth_conf(struct dpp_connection *conn, |
835 | | const u8 *hdr, const u8 *buf, size_t len) |
836 | 0 | { |
837 | 0 | struct dpp_authentication *auth = conn->auth; |
838 | |
|
839 | 0 | wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation"); |
840 | |
|
841 | 0 | if (!auth) { |
842 | 0 | wpa_printf(MSG_DEBUG, |
843 | 0 | "DPP: No DPP Authentication in progress - drop"); |
844 | 0 | return -1; |
845 | 0 | } |
846 | | |
847 | 0 | if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) { |
848 | 0 | wpa_printf(MSG_DEBUG, "DPP: Authentication failed"); |
849 | 0 | return -1; |
850 | 0 | } |
851 | | |
852 | 0 | dpp_controller_auth_success(conn, 0); |
853 | 0 | return 0; |
854 | 0 | } |
855 | | |
856 | | |
857 | | void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx, |
858 | | void *timeout_ctx) |
859 | 0 | { |
860 | 0 | struct dpp_connection *conn = eloop_ctx; |
861 | |
|
862 | 0 | if (!conn->auth->waiting_conf_result) |
863 | 0 | return; |
864 | | |
865 | 0 | wpa_printf(MSG_DEBUG, |
866 | 0 | "DPP: Timeout while waiting for Connection Status Result"); |
867 | 0 | wpa_msg(conn->msg_ctx, MSG_INFO, |
868 | 0 | DPP_EVENT_CONN_STATUS_RESULT "timeout"); |
869 | 0 | dpp_connection_remove(conn); |
870 | 0 | } |
871 | | |
872 | | |
873 | | static int dpp_controller_rx_conf_result(struct dpp_connection *conn, |
874 | | const u8 *hdr, const u8 *buf, |
875 | | size_t len) |
876 | 0 | { |
877 | 0 | struct dpp_authentication *auth = conn->auth; |
878 | 0 | enum dpp_status_error status; |
879 | 0 | void *msg_ctx = conn->msg_ctx; |
880 | |
|
881 | 0 | if (!conn->ctrl && (!auth || !auth->configurator)) |
882 | 0 | return 0; |
883 | | |
884 | 0 | wpa_printf(MSG_DEBUG, "DPP: Configuration Result"); |
885 | |
|
886 | 0 | if (!auth || !auth->waiting_conf_result) { |
887 | 0 | wpa_printf(MSG_DEBUG, |
888 | 0 | "DPP: No DPP Configuration waiting for result - drop"); |
889 | 0 | return -1; |
890 | 0 | } |
891 | | |
892 | 0 | status = dpp_conf_result_rx(auth, hdr, buf, len); |
893 | 0 | if (status == DPP_STATUS_OK && auth->send_conn_status) { |
894 | 0 | wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT |
895 | 0 | "wait_conn_status=1 conf_resp_status=%d", |
896 | 0 | auth->conf_resp_status); |
897 | 0 | wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result"); |
898 | 0 | auth->waiting_conn_status_result = 1; |
899 | 0 | eloop_cancel_timeout( |
900 | 0 | dpp_controller_conn_status_result_wait_timeout, |
901 | 0 | conn, NULL); |
902 | 0 | eloop_register_timeout( |
903 | 0 | 16, 0, dpp_controller_conn_status_result_wait_timeout, |
904 | 0 | conn, NULL); |
905 | 0 | return 0; |
906 | 0 | } |
907 | 0 | if (status == DPP_STATUS_OK) |
908 | 0 | wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT |
909 | 0 | "conf_resp_status=%d", auth->conf_resp_status); |
910 | 0 | else |
911 | 0 | wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); |
912 | 0 | return -1; /* to remove the completed connection */ |
913 | 0 | } |
914 | | |
915 | | |
916 | | static int dpp_controller_rx_conn_status_result(struct dpp_connection *conn, |
917 | | const u8 *hdr, const u8 *buf, |
918 | | size_t len) |
919 | 0 | { |
920 | 0 | struct dpp_authentication *auth = conn->auth; |
921 | 0 | enum dpp_status_error status; |
922 | 0 | u8 ssid[SSID_MAX_LEN]; |
923 | 0 | size_t ssid_len = 0; |
924 | 0 | char *channel_list = NULL; |
925 | |
|
926 | 0 | if (!conn->ctrl) |
927 | 0 | return 0; |
928 | | |
929 | 0 | wpa_printf(MSG_DEBUG, "DPP: Connection Status Result"); |
930 | |
|
931 | 0 | if (!auth || !auth->waiting_conn_status_result) { |
932 | 0 | wpa_printf(MSG_DEBUG, |
933 | 0 | "DPP: No DPP Configuration waiting for connection status result - drop"); |
934 | 0 | return -1; |
935 | 0 | } |
936 | | |
937 | 0 | status = dpp_conn_status_result_rx(auth, hdr, buf, len, |
938 | 0 | ssid, &ssid_len, &channel_list); |
939 | 0 | wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONN_STATUS_RESULT |
940 | 0 | "result=%d ssid=%s channel_list=%s", |
941 | 0 | status, wpa_ssid_txt(ssid, ssid_len), |
942 | 0 | channel_list ? channel_list : "N/A"); |
943 | 0 | os_free(channel_list); |
944 | 0 | return -1; /* to remove the completed connection */ |
945 | 0 | } |
946 | | |
947 | | |
948 | | static int dpp_controller_rx_presence_announcement(struct dpp_connection *conn, |
949 | | const u8 *hdr, const u8 *buf, |
950 | | size_t len) |
951 | 0 | { |
952 | 0 | const u8 *r_bootstrap; |
953 | 0 | u16 r_bootstrap_len; |
954 | 0 | struct dpp_bootstrap_info *peer_bi; |
955 | 0 | struct dpp_authentication *auth; |
956 | 0 | struct dpp_global *dpp = conn->ctrl->global; |
957 | |
|
958 | 0 | wpa_printf(MSG_DEBUG, "DPP: Presence Announcement"); |
959 | |
|
960 | 0 | r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH, |
961 | 0 | &r_bootstrap_len); |
962 | 0 | if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { |
963 | 0 | wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL |
964 | 0 | "Missing or invalid required Responder Bootstrapping Key Hash attribute"); |
965 | 0 | return -1; |
966 | 0 | } |
967 | 0 | wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash", |
968 | 0 | r_bootstrap, r_bootstrap_len); |
969 | 0 | peer_bi = dpp_bootstrap_find_chirp(dpp, r_bootstrap); |
970 | 0 | if (!peer_bi) { |
971 | 0 | wpa_printf(MSG_DEBUG, |
972 | 0 | "DPP: No matching bootstrapping information found"); |
973 | 0 | return -1; |
974 | 0 | } |
975 | | |
976 | 0 | if (conn->auth) { |
977 | 0 | wpa_printf(MSG_DEBUG, |
978 | 0 | "DPP: Ignore Presence Announcement during ongoing Authentication"); |
979 | 0 | return 0; |
980 | 0 | } |
981 | | |
982 | 0 | auth = dpp_auth_init(dpp, conn->msg_ctx, peer_bi, NULL, |
983 | 0 | DPP_CAPAB_CONFIGURATOR, -1, NULL, 0); |
984 | 0 | if (!auth) |
985 | 0 | return -1; |
986 | 0 | if (dpp_set_configurator(auth, conn->ctrl->configurator_params) < 0) { |
987 | 0 | dpp_auth_deinit(auth); |
988 | 0 | return -1; |
989 | 0 | } |
990 | | |
991 | 0 | conn->auth = auth; |
992 | 0 | return dpp_tcp_send_msg(conn, conn->auth->req_msg); |
993 | 0 | } |
994 | | |
995 | | |
996 | | static int dpp_controller_rx_reconfig_announcement(struct dpp_connection *conn, |
997 | | const u8 *hdr, const u8 *buf, |
998 | | size_t len) |
999 | 0 | { |
1000 | 0 | const u8 *csign_hash, *fcgroup, *a_nonce, *e_id; |
1001 | 0 | u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len; |
1002 | 0 | struct dpp_configurator *conf; |
1003 | 0 | struct dpp_global *dpp = conn->ctrl->global; |
1004 | 0 | struct dpp_authentication *auth; |
1005 | 0 | u16 group; |
1006 | |
|
1007 | 0 | if (conn->auth) { |
1008 | 0 | wpa_printf(MSG_DEBUG, |
1009 | 0 | "DPP: Ignore Reconfig Announcement during ongoing Authentication"); |
1010 | 0 | return -1; |
1011 | 0 | } |
1012 | | |
1013 | 0 | wpa_printf(MSG_DEBUG, "DPP: Reconfig Announcement"); |
1014 | |
|
1015 | 0 | csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH, |
1016 | 0 | &csign_hash_len); |
1017 | 0 | if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) { |
1018 | 0 | wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL |
1019 | 0 | "Missing or invalid required Configurator C-sign key Hash attribute"); |
1020 | 0 | return -1; |
1021 | 0 | } |
1022 | 0 | wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator C-sign key Hash (kid)", |
1023 | 0 | csign_hash, csign_hash_len); |
1024 | 0 | conf = dpp_configurator_find_kid(dpp, csign_hash); |
1025 | 0 | if (!conf) { |
1026 | 0 | wpa_printf(MSG_DEBUG, |
1027 | 0 | "DPP: No matching Configurator information found"); |
1028 | 0 | return -1; |
1029 | 0 | } |
1030 | | |
1031 | 0 | fcgroup = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP, |
1032 | 0 | &fcgroup_len); |
1033 | 0 | if (!fcgroup || fcgroup_len != 2) { |
1034 | 0 | wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL |
1035 | 0 | "Missing or invalid required Finite Cyclic Group attribute"); |
1036 | 0 | return -1; |
1037 | 0 | } |
1038 | 0 | group = WPA_GET_LE16(fcgroup); |
1039 | 0 | wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group); |
1040 | |
|
1041 | 0 | a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len); |
1042 | 0 | e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len); |
1043 | |
|
1044 | 0 | auth = dpp_reconfig_init(dpp, conn->msg_ctx, conf, 0, group, |
1045 | 0 | a_nonce, a_nonce_len, e_id, e_id_len); |
1046 | 0 | if (!auth) |
1047 | 0 | return -1; |
1048 | 0 | if (dpp_set_configurator(auth, conn->ctrl->configurator_params) < 0) { |
1049 | 0 | dpp_auth_deinit(auth); |
1050 | 0 | return -1; |
1051 | 0 | } |
1052 | | |
1053 | 0 | conn->auth = auth; |
1054 | 0 | return dpp_tcp_send_msg(conn, auth->reconfig_req_msg); |
1055 | 0 | } |
1056 | | |
1057 | | |
1058 | | static int dpp_controller_rx_reconfig_auth_resp(struct dpp_connection *conn, |
1059 | | const u8 *hdr, const u8 *buf, |
1060 | | size_t len) |
1061 | 0 | { |
1062 | 0 | struct dpp_authentication *auth = conn->auth; |
1063 | 0 | struct wpabuf *conf; |
1064 | 0 | int res; |
1065 | |
|
1066 | 0 | wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response"); |
1067 | |
|
1068 | 0 | if (!auth || !auth->reconfig || !auth->configurator) { |
1069 | 0 | wpa_printf(MSG_DEBUG, |
1070 | 0 | "DPP: No DPP Reconfig Authentication in progress - drop"); |
1071 | 0 | return -1; |
1072 | 0 | } |
1073 | | |
1074 | 0 | conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len); |
1075 | 0 | if (!conf) |
1076 | 0 | return -1; |
1077 | | |
1078 | 0 | res = dpp_tcp_send_msg(conn, conf); |
1079 | 0 | wpabuf_free(conf); |
1080 | 0 | return res; |
1081 | 0 | } |
1082 | | |
1083 | | |
1084 | | static int dpp_controller_rx_pkex_exchange_req(struct dpp_connection *conn, |
1085 | | const u8 *hdr, const u8 *buf, |
1086 | | size_t len) |
1087 | 0 | { |
1088 | 0 | struct dpp_controller *ctrl = conn->ctrl; |
1089 | |
|
1090 | 0 | if (!ctrl) |
1091 | 0 | return 0; |
1092 | | |
1093 | 0 | wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request"); |
1094 | | |
1095 | | /* TODO: Support multiple PKEX codes by iterating over all the enabled |
1096 | | * values here */ |
1097 | |
|
1098 | 0 | if (!ctrl->pkex_code || !ctrl->pkex_bi) { |
1099 | 0 | wpa_printf(MSG_DEBUG, |
1100 | 0 | "DPP: No PKEX code configured - ignore request"); |
1101 | 0 | return 0; |
1102 | 0 | } |
1103 | | |
1104 | 0 | if (conn->pkex || conn->auth) { |
1105 | 0 | wpa_printf(MSG_DEBUG, |
1106 | 0 | "DPP: Already in PKEX/Authentication session - ignore new PKEX request"); |
1107 | 0 | return 0; |
1108 | 0 | } |
1109 | | |
1110 | 0 | conn->pkex = dpp_pkex_rx_exchange_req(conn->msg_ctx, ctrl->pkex_bi, |
1111 | 0 | NULL, NULL, |
1112 | 0 | ctrl->pkex_identifier, |
1113 | 0 | ctrl->pkex_code, |
1114 | 0 | os_strlen(ctrl->pkex_code), |
1115 | 0 | buf, len, true); |
1116 | 0 | if (!conn->pkex) { |
1117 | 0 | wpa_printf(MSG_DEBUG, |
1118 | 0 | "DPP: Failed to process the request"); |
1119 | 0 | return -1; |
1120 | 0 | } |
1121 | | |
1122 | 0 | return dpp_tcp_send_msg(conn, conn->pkex->exchange_resp); |
1123 | 0 | } |
1124 | | |
1125 | | |
1126 | | static int dpp_controller_rx_pkex_exchange_resp(struct dpp_connection *conn, |
1127 | | const u8 *hdr, const u8 *buf, |
1128 | | size_t len) |
1129 | 0 | { |
1130 | 0 | struct dpp_pkex *pkex = conn->pkex; |
1131 | 0 | struct wpabuf *msg; |
1132 | 0 | int res; |
1133 | |
|
1134 | 0 | wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response"); |
1135 | |
|
1136 | 0 | if (!pkex || !pkex->initiator || pkex->exchange_done) { |
1137 | 0 | wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); |
1138 | 0 | return 0; |
1139 | 0 | } |
1140 | | |
1141 | 0 | msg = dpp_pkex_rx_exchange_resp(pkex, NULL, buf, len); |
1142 | 0 | if (!msg) { |
1143 | 0 | wpa_printf(MSG_DEBUG, "DPP: Failed to process the response"); |
1144 | 0 | return -1; |
1145 | 0 | } |
1146 | | |
1147 | 0 | wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request"); |
1148 | 0 | res = dpp_tcp_send_msg(conn, msg); |
1149 | 0 | wpabuf_free(msg); |
1150 | 0 | return res; |
1151 | 0 | } |
1152 | | |
1153 | | |
1154 | | static int dpp_controller_rx_pkex_commit_reveal_req(struct dpp_connection *conn, |
1155 | | const u8 *hdr, |
1156 | | const u8 *buf, size_t len) |
1157 | 0 | { |
1158 | 0 | struct dpp_pkex *pkex = conn->pkex; |
1159 | 0 | struct wpabuf *msg; |
1160 | 0 | int res; |
1161 | 0 | struct dpp_bootstrap_info *bi; |
1162 | |
|
1163 | 0 | wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request"); |
1164 | |
|
1165 | 0 | if (!pkex || pkex->initiator || !pkex->exchange_done) { |
1166 | 0 | wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); |
1167 | 0 | return 0; |
1168 | 0 | } |
1169 | | |
1170 | 0 | msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len); |
1171 | 0 | if (!msg) { |
1172 | 0 | wpa_printf(MSG_DEBUG, "DPP: Failed to process the request"); |
1173 | 0 | return -1; |
1174 | 0 | } |
1175 | | |
1176 | 0 | wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response"); |
1177 | 0 | res = dpp_tcp_send_msg(conn, msg); |
1178 | 0 | wpabuf_free(msg); |
1179 | 0 | if (res < 0) |
1180 | 0 | return res; |
1181 | 0 | bi = dpp_pkex_finish(conn->global, pkex, NULL, 0); |
1182 | 0 | if (!bi) |
1183 | 0 | return -1; |
1184 | 0 | conn->pkex = NULL; |
1185 | 0 | return 0; |
1186 | 0 | } |
1187 | | |
1188 | | |
1189 | | static int |
1190 | | dpp_controller_rx_pkex_commit_reveal_resp(struct dpp_connection *conn, |
1191 | | const u8 *hdr, |
1192 | | const u8 *buf, size_t len) |
1193 | 0 | { |
1194 | 0 | struct dpp_pkex *pkex = conn->pkex; |
1195 | 0 | int res; |
1196 | 0 | struct dpp_bootstrap_info *bi; |
1197 | |
|
1198 | 0 | wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response"); |
1199 | |
|
1200 | 0 | if (!pkex || !pkex->initiator || !pkex->exchange_done) { |
1201 | 0 | wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); |
1202 | 0 | return 0; |
1203 | 0 | } |
1204 | | |
1205 | 0 | res = dpp_pkex_rx_commit_reveal_resp(pkex, hdr, buf, len); |
1206 | 0 | if (res < 0) { |
1207 | 0 | wpa_printf(MSG_DEBUG, "DPP: Failed to process the response"); |
1208 | 0 | return res; |
1209 | 0 | } |
1210 | | |
1211 | 0 | bi = dpp_pkex_finish(conn->global, pkex, NULL, 0); |
1212 | 0 | if (!bi) |
1213 | 0 | return -1; |
1214 | 0 | conn->pkex = NULL; |
1215 | |
|
1216 | 0 | if (!conn->pkex_done) |
1217 | 0 | return -1; |
1218 | 0 | return conn->pkex_done(conn->cb_ctx, conn, bi); |
1219 | 0 | } |
1220 | | |
1221 | | |
1222 | | static int dpp_controller_rx_action(struct dpp_connection *conn, const u8 *msg, |
1223 | | size_t len) |
1224 | 0 | { |
1225 | 0 | const u8 *pos, *end; |
1226 | 0 | u8 type; |
1227 | |
|
1228 | 0 | wpa_printf(MSG_DEBUG, "DPP: Received DPP Action frame over TCP"); |
1229 | 0 | pos = msg; |
1230 | 0 | end = msg + len; |
1231 | |
|
1232 | 0 | if (end - pos < DPP_HDR_LEN || |
1233 | 0 | WPA_GET_BE24(pos) != OUI_WFA || |
1234 | 0 | pos[3] != DPP_OUI_TYPE) { |
1235 | 0 | wpa_printf(MSG_DEBUG, "DPP: Unrecognized header"); |
1236 | 0 | return -1; |
1237 | 0 | } |
1238 | | |
1239 | 0 | if (pos[4] != 1) { |
1240 | 0 | wpa_printf(MSG_DEBUG, "DPP: Unsupported Crypto Suite %u", |
1241 | 0 | pos[4]); |
1242 | 0 | return -1; |
1243 | 0 | } |
1244 | 0 | type = pos[5]; |
1245 | 0 | wpa_printf(MSG_DEBUG, "DPP: Received message type %u", type); |
1246 | 0 | pos += DPP_HDR_LEN; |
1247 | |
|
1248 | 0 | wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", |
1249 | 0 | pos, end - pos); |
1250 | 0 | if (dpp_check_attrs(pos, end - pos) < 0) |
1251 | 0 | return -1; |
1252 | | |
1253 | 0 | if (conn->relay) { |
1254 | 0 | wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN"); |
1255 | 0 | conn->relay->tx(conn->relay->cb_ctx, conn->mac_addr, |
1256 | 0 | conn->freq, msg, len); |
1257 | 0 | return 0; |
1258 | 0 | } |
1259 | | |
1260 | 0 | switch (type) { |
1261 | 0 | case DPP_PA_AUTHENTICATION_REQ: |
1262 | 0 | return dpp_controller_rx_auth_req(conn, msg, pos, end - pos); |
1263 | 0 | case DPP_PA_AUTHENTICATION_RESP: |
1264 | 0 | return dpp_controller_rx_auth_resp(conn, msg, pos, end - pos); |
1265 | 0 | case DPP_PA_AUTHENTICATION_CONF: |
1266 | 0 | return dpp_controller_rx_auth_conf(conn, msg, pos, end - pos); |
1267 | 0 | case DPP_PA_CONFIGURATION_RESULT: |
1268 | 0 | return dpp_controller_rx_conf_result(conn, msg, pos, end - pos); |
1269 | 0 | case DPP_PA_CONNECTION_STATUS_RESULT: |
1270 | 0 | return dpp_controller_rx_conn_status_result(conn, msg, pos, |
1271 | 0 | end - pos); |
1272 | 0 | case DPP_PA_PRESENCE_ANNOUNCEMENT: |
1273 | 0 | return dpp_controller_rx_presence_announcement(conn, msg, pos, |
1274 | 0 | end - pos); |
1275 | 0 | case DPP_PA_RECONFIG_ANNOUNCEMENT: |
1276 | 0 | return dpp_controller_rx_reconfig_announcement(conn, msg, pos, |
1277 | 0 | end - pos); |
1278 | 0 | case DPP_PA_RECONFIG_AUTH_RESP: |
1279 | 0 | return dpp_controller_rx_reconfig_auth_resp(conn, msg, pos, |
1280 | 0 | end - pos); |
1281 | 0 | case DPP_PA_PKEX_V1_EXCHANGE_REQ: |
1282 | 0 | wpa_printf(MSG_DEBUG, |
1283 | 0 | "DPP: Ignore PKEXv1 Exchange Request - not supported over TCP"); |
1284 | 0 | return -1; |
1285 | 0 | case DPP_PA_PKEX_EXCHANGE_REQ: |
1286 | 0 | return dpp_controller_rx_pkex_exchange_req(conn, msg, pos, |
1287 | 0 | end - pos); |
1288 | 0 | case DPP_PA_PKEX_EXCHANGE_RESP: |
1289 | 0 | return dpp_controller_rx_pkex_exchange_resp(conn, msg, pos, |
1290 | 0 | end - pos); |
1291 | 0 | case DPP_PA_PKEX_COMMIT_REVEAL_REQ: |
1292 | 0 | return dpp_controller_rx_pkex_commit_reveal_req(conn, msg, pos, |
1293 | 0 | end - pos); |
1294 | 0 | case DPP_PA_PKEX_COMMIT_REVEAL_RESP: |
1295 | 0 | return dpp_controller_rx_pkex_commit_reveal_resp(conn, msg, pos, |
1296 | 0 | end - pos); |
1297 | 0 | default: |
1298 | | /* TODO: missing messages types */ |
1299 | 0 | wpa_printf(MSG_DEBUG, |
1300 | 0 | "DPP: Unsupported frame subtype %d", type); |
1301 | 0 | return -1; |
1302 | 0 | } |
1303 | 0 | } |
1304 | | |
1305 | | |
1306 | | static int dpp_tcp_send_comeback_delay(struct dpp_connection *conn, u8 action) |
1307 | 0 | { |
1308 | 0 | struct wpabuf *buf; |
1309 | 0 | size_t len = 18; |
1310 | |
|
1311 | 0 | if (action == WLAN_PA_GAS_COMEBACK_RESP) |
1312 | 0 | len++; |
1313 | |
|
1314 | 0 | buf = wpabuf_alloc(4 + len); |
1315 | 0 | if (!buf) |
1316 | 0 | return -1; |
1317 | | |
1318 | 0 | wpabuf_put_be32(buf, len); |
1319 | |
|
1320 | 0 | wpabuf_put_u8(buf, action); |
1321 | 0 | wpabuf_put_u8(buf, conn->gas_dialog_token); |
1322 | 0 | wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); |
1323 | 0 | if (action == WLAN_PA_GAS_COMEBACK_RESP) |
1324 | 0 | wpabuf_put_u8(buf, 0); |
1325 | 0 | wpabuf_put_le16(buf, 500); /* GAS Comeback Delay */ |
1326 | |
|
1327 | 0 | dpp_write_adv_proto(buf); |
1328 | 0 | wpabuf_put_le16(buf, 0); /* Query Response Length */ |
1329 | | |
1330 | | /* Send Config Response over TCP */ |
1331 | 0 | wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf); |
1332 | 0 | wpabuf_free(conn->msg_out); |
1333 | 0 | conn->msg_out_pos = 0; |
1334 | 0 | conn->msg_out = buf; |
1335 | 0 | dpp_tcp_send(conn); |
1336 | 0 | return 0; |
1337 | 0 | } |
1338 | | |
1339 | | |
1340 | | static int dpp_tcp_send_gas_resp(struct dpp_connection *conn, u8 action, |
1341 | | struct wpabuf *resp) |
1342 | 0 | { |
1343 | 0 | struct wpabuf *buf; |
1344 | 0 | size_t len; |
1345 | |
|
1346 | 0 | if (!resp) |
1347 | 0 | return -1; |
1348 | | |
1349 | 0 | len = 18 + wpabuf_len(resp); |
1350 | 0 | if (action == WLAN_PA_GAS_COMEBACK_RESP) |
1351 | 0 | len++; |
1352 | |
|
1353 | 0 | buf = wpabuf_alloc(4 + len); |
1354 | 0 | if (!buf) { |
1355 | 0 | wpabuf_free(resp); |
1356 | 0 | return -1; |
1357 | 0 | } |
1358 | | |
1359 | 0 | wpabuf_put_be32(buf, len); |
1360 | |
|
1361 | 0 | wpabuf_put_u8(buf, action); |
1362 | 0 | wpabuf_put_u8(buf, conn->gas_dialog_token); |
1363 | 0 | wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); |
1364 | 0 | if (action == WLAN_PA_GAS_COMEBACK_RESP) |
1365 | 0 | wpabuf_put_u8(buf, 0); |
1366 | 0 | wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */ |
1367 | |
|
1368 | 0 | dpp_write_adv_proto(buf); |
1369 | 0 | dpp_write_gas_query(buf, resp); |
1370 | 0 | wpabuf_free(resp); |
1371 | | |
1372 | | /* Send Config Response over TCP; GAS fragmentation is taken care of by |
1373 | | * the Relay */ |
1374 | 0 | wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf); |
1375 | 0 | wpabuf_free(conn->msg_out); |
1376 | 0 | conn->msg_out_pos = 0; |
1377 | 0 | conn->msg_out = buf; |
1378 | 0 | conn->on_tcp_tx_complete_gas_done = 1; |
1379 | 0 | dpp_tcp_send(conn); |
1380 | 0 | return 0; |
1381 | 0 | } |
1382 | | |
1383 | | |
1384 | | static int dpp_controller_rx_gas_req(struct dpp_connection *conn, const u8 *msg, |
1385 | | size_t len) |
1386 | 0 | { |
1387 | 0 | const u8 *pos, *end, *next; |
1388 | 0 | const u8 *adv_proto; |
1389 | 0 | u16 slen; |
1390 | 0 | struct wpabuf *resp; |
1391 | 0 | struct dpp_authentication *auth = conn->auth; |
1392 | |
|
1393 | 0 | if (len < 1 + 2) |
1394 | 0 | return -1; |
1395 | | |
1396 | 0 | wpa_printf(MSG_DEBUG, |
1397 | 0 | "DPP: Received DPP Configuration Request over TCP"); |
1398 | |
|
1399 | 0 | if (!auth || (!conn->ctrl && !auth->configurator) || |
1400 | 0 | (!auth->auth_success && !auth->reconfig_success)) { |
1401 | 0 | wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); |
1402 | 0 | return -1; |
1403 | 0 | } |
1404 | | |
1405 | 0 | wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONF_REQ_RX); |
1406 | |
|
1407 | 0 | pos = msg; |
1408 | 0 | end = msg + len; |
1409 | |
|
1410 | 0 | conn->gas_dialog_token = *pos++; |
1411 | 0 | adv_proto = pos++; |
1412 | 0 | slen = *pos++; |
1413 | 0 | if (*adv_proto != WLAN_EID_ADV_PROTO || |
1414 | 0 | slen > end - pos || slen < 2) |
1415 | 0 | return -1; |
1416 | | |
1417 | 0 | next = pos + slen; |
1418 | 0 | pos++; /* skip QueryRespLenLimit and PAME-BI */ |
1419 | |
|
1420 | 0 | if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC || |
1421 | 0 | pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA || |
1422 | 0 | pos[5] != DPP_OUI_TYPE || pos[6] != 0x01) |
1423 | 0 | return -1; |
1424 | | |
1425 | 0 | pos = next; |
1426 | | /* Query Request */ |
1427 | 0 | if (end - pos < 2) |
1428 | 0 | return -1; |
1429 | 0 | slen = WPA_GET_LE16(pos); |
1430 | 0 | pos += 2; |
1431 | 0 | if (slen > end - pos) |
1432 | 0 | return -1; |
1433 | | |
1434 | 0 | resp = dpp_conf_req_rx(auth, pos, slen); |
1435 | 0 | if (!resp && auth->waiting_cert) { |
1436 | 0 | wpa_printf(MSG_DEBUG, "DPP: Certificate not yet ready"); |
1437 | 0 | conn->gas_comeback_in_progress = 1; |
1438 | 0 | return dpp_tcp_send_comeback_delay(conn, |
1439 | 0 | WLAN_PA_GAS_INITIAL_RESP); |
1440 | 0 | } |
1441 | | |
1442 | 0 | if (!resp && auth->waiting_config && auth->peer_bi) { |
1443 | 0 | char *buf = NULL, *name = ""; |
1444 | 0 | char band[200], *b_pos, *b_end; |
1445 | 0 | int i, res, *opclass = auth->e_band_support; |
1446 | 0 | char *mud_url = "N/A"; |
1447 | |
|
1448 | 0 | wpa_printf(MSG_DEBUG, "DPP: Configuration not yet ready"); |
1449 | 0 | if (auth->e_name) { |
1450 | 0 | size_t e_len = os_strlen(auth->e_name); |
1451 | |
|
1452 | 0 | buf = os_malloc(e_len * 4 + 1); |
1453 | 0 | if (buf) { |
1454 | 0 | printf_encode(buf, len * 4 + 1, |
1455 | 0 | (const u8 *) auth->e_name, e_len); |
1456 | 0 | name = buf; |
1457 | 0 | } |
1458 | 0 | } |
1459 | 0 | band[0] = '\0'; |
1460 | 0 | b_pos = band; |
1461 | 0 | b_end = band + sizeof(band); |
1462 | 0 | for (i = 0; opclass && opclass[i]; i++) { |
1463 | 0 | res = os_snprintf(b_pos, b_end - b_pos, "%s%d", |
1464 | 0 | b_pos == band ? "" : ",", opclass[i]); |
1465 | 0 | if (os_snprintf_error(b_end - b_pos, res)) { |
1466 | 0 | *b_pos = '\0'; |
1467 | 0 | break; |
1468 | 0 | } |
1469 | 0 | b_pos += res; |
1470 | 0 | } |
1471 | 0 | if (auth->e_mud_url) { |
1472 | 0 | size_t e_len = os_strlen(auth->e_mud_url); |
1473 | |
|
1474 | 0 | if (!has_ctrl_char((const u8 *) auth->e_mud_url, e_len)) |
1475 | 0 | mud_url = auth->e_mud_url; |
1476 | 0 | } |
1477 | 0 | wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONF_NEEDED |
1478 | 0 | "peer=%d net_role=%s name=\"%s\" opclass=%s mud_url=%s", |
1479 | 0 | auth->peer_bi->id, dpp_netrole_str(auth->e_netrole), |
1480 | 0 | name, band, mud_url); |
1481 | 0 | os_free(buf); |
1482 | |
|
1483 | 0 | conn->gas_comeback_in_progress = 1; |
1484 | 0 | return dpp_tcp_send_comeback_delay(conn, |
1485 | 0 | WLAN_PA_GAS_INITIAL_RESP); |
1486 | 0 | } |
1487 | | |
1488 | 0 | return dpp_tcp_send_gas_resp(conn, WLAN_PA_GAS_INITIAL_RESP, resp); |
1489 | 0 | } |
1490 | | |
1491 | | |
1492 | | static int dpp_controller_rx_gas_comeback_req(struct dpp_connection *conn, |
1493 | | const u8 *msg, size_t len) |
1494 | 0 | { |
1495 | 0 | u8 dialog_token; |
1496 | 0 | struct dpp_authentication *auth = conn->auth; |
1497 | 0 | struct wpabuf *resp; |
1498 | |
|
1499 | 0 | if (len < 1) |
1500 | 0 | return -1; |
1501 | | |
1502 | 0 | wpa_printf(MSG_DEBUG, |
1503 | 0 | "DPP: Received DPP Configuration Request over TCP (comeback)"); |
1504 | |
|
1505 | 0 | if (!auth || (!conn->ctrl && !auth->configurator) || |
1506 | 0 | (!auth->auth_success && !auth->reconfig_success) || |
1507 | 0 | !conn->gas_comeback_in_progress) { |
1508 | 0 | wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); |
1509 | 0 | return -1; |
1510 | 0 | } |
1511 | | |
1512 | 0 | dialog_token = msg[0]; |
1513 | 0 | if (dialog_token != conn->gas_dialog_token) { |
1514 | 0 | wpa_printf(MSG_DEBUG, "DPP: Dialog token mismatch (%u != %u)", |
1515 | 0 | dialog_token, conn->gas_dialog_token); |
1516 | 0 | return -1; |
1517 | 0 | } |
1518 | | |
1519 | 0 | if (!auth->conf_resp_tcp) { |
1520 | 0 | wpa_printf(MSG_DEBUG, "DPP: Certificate not yet ready"); |
1521 | 0 | return dpp_tcp_send_comeback_delay(conn, |
1522 | 0 | WLAN_PA_GAS_COMEBACK_RESP); |
1523 | 0 | } |
1524 | | |
1525 | 0 | wpa_printf(MSG_DEBUG, |
1526 | 0 | "DPP: Configuration response is ready to be sent out"); |
1527 | 0 | resp = auth->conf_resp_tcp; |
1528 | 0 | auth->conf_resp_tcp = NULL; |
1529 | 0 | return dpp_tcp_send_gas_resp(conn, WLAN_PA_GAS_COMEBACK_RESP, resp); |
1530 | 0 | } |
1531 | | |
1532 | | |
1533 | | static void dpp_tcp_build_csr(void *eloop_ctx, void *timeout_ctx) |
1534 | 0 | { |
1535 | 0 | struct dpp_connection *conn = eloop_ctx; |
1536 | 0 | struct dpp_authentication *auth = conn->auth; |
1537 | |
|
1538 | 0 | if (!auth || !auth->csrattrs) |
1539 | 0 | return; |
1540 | | |
1541 | 0 | wpa_printf(MSG_DEBUG, "DPP: Build CSR"); |
1542 | 0 | wpabuf_free(auth->csr); |
1543 | | /* TODO: Additional information needed for CSR based on csrAttrs */ |
1544 | 0 | auth->csr = dpp_build_csr(auth, conn->name ? conn->name : "Test"); |
1545 | 0 | if (!auth->csr) { |
1546 | 0 | dpp_connection_remove(conn); |
1547 | 0 | return; |
1548 | 0 | } |
1549 | | |
1550 | 0 | dpp_controller_start_gas_client(conn); |
1551 | 0 | } |
1552 | | |
1553 | | |
1554 | | #ifdef CONFIG_DPP3 |
1555 | | static void dpp_tcp_build_new_key(void *eloop_ctx, void *timeout_ctx) |
1556 | | { |
1557 | | struct dpp_connection *conn = eloop_ctx; |
1558 | | struct dpp_authentication *auth = conn->auth; |
1559 | | |
1560 | | if (!auth || !auth->waiting_new_key) |
1561 | | return; |
1562 | | |
1563 | | wpa_printf(MSG_DEBUG, "DPP: Build config request with a new key"); |
1564 | | dpp_controller_start_gas_client(conn); |
1565 | | } |
1566 | | #endif /* CONFIG_DPP3 */ |
1567 | | |
1568 | | |
1569 | | static int dpp_tcp_rx_gas_resp(struct dpp_connection *conn, struct wpabuf *resp) |
1570 | 0 | { |
1571 | 0 | struct dpp_authentication *auth = conn->auth; |
1572 | 0 | int res; |
1573 | 0 | struct wpabuf *msg; |
1574 | 0 | enum dpp_status_error status; |
1575 | |
|
1576 | 0 | wpa_printf(MSG_DEBUG, |
1577 | 0 | "DPP: Configuration Response for local stack from TCP"); |
1578 | |
|
1579 | 0 | if (auth) |
1580 | 0 | res = dpp_conf_resp_rx(auth, resp); |
1581 | 0 | else |
1582 | 0 | res = -1; |
1583 | 0 | wpabuf_free(resp); |
1584 | 0 | if (res == -2) { |
1585 | 0 | wpa_printf(MSG_DEBUG, "DPP: CSR needed"); |
1586 | 0 | eloop_register_timeout(0, 0, dpp_tcp_build_csr, conn, NULL); |
1587 | 0 | return 0; |
1588 | 0 | } |
1589 | | #ifdef CONFIG_DPP3 |
1590 | | if (res == -3) { |
1591 | | wpa_printf(MSG_DEBUG, "DPP: New protocol key needed"); |
1592 | | eloop_register_timeout(0, 0, dpp_tcp_build_new_key, conn, |
1593 | | NULL); |
1594 | | return 0; |
1595 | | } |
1596 | | #endif /* CONFIG_DPP3 */ |
1597 | 0 | if (res < 0) { |
1598 | 0 | wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed"); |
1599 | 0 | return -1; |
1600 | 0 | } |
1601 | | |
1602 | 0 | if (conn->process_conf_obj) |
1603 | 0 | res = conn->process_conf_obj(conn->cb_ctx, auth); |
1604 | 0 | else |
1605 | 0 | res = 0; |
1606 | |
|
1607 | 0 | if (auth->peer_version < 2 || auth->conf_resp_status != DPP_STATUS_OK) |
1608 | 0 | return -1; |
1609 | | |
1610 | 0 | wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result"); |
1611 | 0 | status = res < 0 ? DPP_STATUS_CONFIG_REJECTED : DPP_STATUS_OK; |
1612 | 0 | msg = dpp_build_conf_result(auth, status); |
1613 | 0 | if (!msg) |
1614 | 0 | return -1; |
1615 | | |
1616 | 0 | conn->on_tcp_tx_complete_remove = 1; |
1617 | 0 | res = dpp_tcp_send_msg(conn, msg); |
1618 | 0 | wpabuf_free(msg); |
1619 | | |
1620 | | /* This exchange will be terminated in the TX status handler */ |
1621 | |
|
1622 | 0 | return res; |
1623 | 0 | } |
1624 | | |
1625 | | |
1626 | | static void dpp_tcp_gas_query_comeback(void *eloop_ctx, void *timeout_ctx) |
1627 | 0 | { |
1628 | 0 | struct dpp_connection *conn = eloop_ctx; |
1629 | 0 | struct dpp_authentication *auth = conn->auth; |
1630 | 0 | struct wpabuf *msg; |
1631 | |
|
1632 | 0 | if (!auth) |
1633 | 0 | return; |
1634 | | |
1635 | 0 | wpa_printf(MSG_DEBUG, "DPP: Send GAS Comeback Request"); |
1636 | 0 | msg = wpabuf_alloc(4 + 2); |
1637 | 0 | if (!msg) |
1638 | 0 | return; |
1639 | 0 | wpabuf_put_be32(msg, 2); |
1640 | 0 | wpabuf_put_u8(msg, WLAN_PA_GAS_COMEBACK_REQ); |
1641 | 0 | wpabuf_put_u8(msg, conn->gas_dialog_token); |
1642 | 0 | wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg); |
1643 | |
|
1644 | 0 | wpabuf_free(conn->msg_out); |
1645 | 0 | conn->msg_out_pos = 0; |
1646 | 0 | conn->msg_out = msg; |
1647 | 0 | dpp_tcp_send(conn); |
1648 | 0 | } |
1649 | | |
1650 | | |
1651 | | static int dpp_rx_gas_resp(struct dpp_connection *conn, const u8 *msg, |
1652 | | size_t len, bool comeback) |
1653 | 0 | { |
1654 | 0 | struct wpabuf *buf; |
1655 | 0 | u8 dialog_token; |
1656 | 0 | const u8 *pos, *end, *next, *adv_proto; |
1657 | 0 | u16 status, slen, comeback_delay; |
1658 | |
|
1659 | 0 | if (len < (size_t) (5 + 2 + (comeback ? 1 : 0))) |
1660 | 0 | return -1; |
1661 | | |
1662 | 0 | wpa_printf(MSG_DEBUG, |
1663 | 0 | "DPP: Received DPP Configuration Response over TCP"); |
1664 | |
|
1665 | 0 | pos = msg; |
1666 | 0 | end = msg + len; |
1667 | |
|
1668 | 0 | dialog_token = *pos++; |
1669 | 0 | status = WPA_GET_LE16(pos); |
1670 | 0 | if (status != WLAN_STATUS_SUCCESS) { |
1671 | 0 | wpa_printf(MSG_DEBUG, "DPP: Unexpected Status Code %u", status); |
1672 | 0 | return -1; |
1673 | 0 | } |
1674 | 0 | pos += 2; |
1675 | 0 | if (comeback) |
1676 | 0 | pos++; /* ignore Fragment ID */ |
1677 | 0 | comeback_delay = WPA_GET_LE16(pos); |
1678 | 0 | pos += 2; |
1679 | |
|
1680 | 0 | adv_proto = pos++; |
1681 | 0 | slen = *pos++; |
1682 | 0 | if (*adv_proto != WLAN_EID_ADV_PROTO || |
1683 | 0 | slen > end - pos || slen < 2) |
1684 | 0 | return -1; |
1685 | | |
1686 | 0 | next = pos + slen; |
1687 | 0 | pos++; /* skip QueryRespLenLimit and PAME-BI */ |
1688 | |
|
1689 | 0 | if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC || |
1690 | 0 | pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA || |
1691 | 0 | pos[5] != DPP_OUI_TYPE || pos[6] != 0x01) |
1692 | 0 | return -1; |
1693 | | |
1694 | 0 | pos = next; |
1695 | | /* Query Response */ |
1696 | 0 | if (end - pos < 2) |
1697 | 0 | return -1; |
1698 | 0 | slen = WPA_GET_LE16(pos); |
1699 | 0 | pos += 2; |
1700 | 0 | if (slen > end - pos) |
1701 | 0 | return -1; |
1702 | | |
1703 | 0 | if (comeback_delay) { |
1704 | 0 | unsigned int secs, usecs; |
1705 | |
|
1706 | 0 | conn->gas_dialog_token = dialog_token; |
1707 | 0 | secs = (comeback_delay * 1024) / 1000000; |
1708 | 0 | usecs = comeback_delay * 1024 - secs * 1000000; |
1709 | 0 | wpa_printf(MSG_DEBUG, "DPP: Comeback delay: %u", |
1710 | 0 | comeback_delay); |
1711 | 0 | eloop_cancel_timeout(dpp_tcp_gas_query_comeback, conn, NULL); |
1712 | 0 | eloop_register_timeout(secs, usecs, dpp_tcp_gas_query_comeback, |
1713 | 0 | conn, NULL); |
1714 | 0 | return 0; |
1715 | 0 | } |
1716 | | |
1717 | 0 | buf = wpabuf_alloc(slen); |
1718 | 0 | if (!buf) |
1719 | 0 | return -1; |
1720 | 0 | wpabuf_put_data(buf, pos, slen); |
1721 | |
|
1722 | 0 | if (!conn->relay && |
1723 | 0 | (!conn->ctrl || (conn->ctrl->allowed_roles & DPP_CAPAB_ENROLLEE))) |
1724 | 0 | return dpp_tcp_rx_gas_resp(conn, buf); |
1725 | | |
1726 | 0 | if (!conn->relay) { |
1727 | 0 | wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); |
1728 | 0 | wpabuf_free(buf); |
1729 | 0 | return -1; |
1730 | 0 | } |
1731 | 0 | wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN"); |
1732 | 0 | conn->relay->gas_resp_tx(conn->relay->cb_ctx, conn->mac_addr, |
1733 | 0 | dialog_token, 0, buf); |
1734 | |
|
1735 | 0 | return 0; |
1736 | 0 | } |
1737 | | |
1738 | | |
1739 | | static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx) |
1740 | 0 | { |
1741 | 0 | struct dpp_connection *conn = eloop_ctx; |
1742 | 0 | int res; |
1743 | 0 | const u8 *pos; |
1744 | |
|
1745 | 0 | wpa_printf(MSG_DEBUG, "DPP: TCP data available for reading (sock %d)", |
1746 | 0 | sd); |
1747 | |
|
1748 | 0 | if (conn->msg_len_octets < 4) { |
1749 | 0 | u32 msglen; |
1750 | |
|
1751 | 0 | res = recv(sd, &conn->msg_len[conn->msg_len_octets], |
1752 | 0 | 4 - conn->msg_len_octets, 0); |
1753 | 0 | if (res < 0) { |
1754 | 0 | wpa_printf(MSG_DEBUG, "DPP: recv failed: %s", |
1755 | 0 | strerror(errno)); |
1756 | 0 | dpp_connection_remove(conn); |
1757 | 0 | return; |
1758 | 0 | } |
1759 | 0 | if (res == 0) { |
1760 | 0 | wpa_printf(MSG_DEBUG, |
1761 | 0 | "DPP: No more data available over TCP"); |
1762 | 0 | dpp_connection_remove(conn); |
1763 | 0 | return; |
1764 | 0 | } |
1765 | 0 | wpa_printf(MSG_DEBUG, |
1766 | 0 | "DPP: Received %d/%d octet(s) of message length field", |
1767 | 0 | res, (int) (4 - conn->msg_len_octets)); |
1768 | 0 | conn->msg_len_octets += res; |
1769 | |
|
1770 | 0 | if (conn->msg_len_octets < 4) { |
1771 | 0 | wpa_printf(MSG_DEBUG, |
1772 | 0 | "DPP: Need %d more octets of message length field", |
1773 | 0 | (int) (4 - conn->msg_len_octets)); |
1774 | 0 | return; |
1775 | 0 | } |
1776 | | |
1777 | 0 | msglen = WPA_GET_BE32(conn->msg_len); |
1778 | 0 | wpa_printf(MSG_DEBUG, "DPP: Message length: %u", msglen); |
1779 | 0 | if (msglen > 65535) { |
1780 | 0 | wpa_printf(MSG_INFO, "DPP: Unexpectedly long message"); |
1781 | 0 | dpp_connection_remove(conn); |
1782 | 0 | return; |
1783 | 0 | } |
1784 | | |
1785 | 0 | wpabuf_free(conn->msg); |
1786 | 0 | conn->msg = wpabuf_alloc(msglen); |
1787 | 0 | } |
1788 | | |
1789 | 0 | if (!conn->msg) { |
1790 | 0 | wpa_printf(MSG_DEBUG, |
1791 | 0 | "DPP: No buffer available for receiving the message"); |
1792 | 0 | dpp_connection_remove(conn); |
1793 | 0 | return; |
1794 | 0 | } |
1795 | | |
1796 | 0 | wpa_printf(MSG_DEBUG, "DPP: Need %u more octets of message payload", |
1797 | 0 | (unsigned int) wpabuf_tailroom(conn->msg)); |
1798 | |
|
1799 | 0 | res = recv(sd, wpabuf_put(conn->msg, 0), wpabuf_tailroom(conn->msg), 0); |
1800 | 0 | if (res < 0) { |
1801 | 0 | wpa_printf(MSG_DEBUG, "DPP: recv failed: %s", strerror(errno)); |
1802 | 0 | dpp_connection_remove(conn); |
1803 | 0 | return; |
1804 | 0 | } |
1805 | 0 | if (res == 0) { |
1806 | 0 | wpa_printf(MSG_DEBUG, "DPP: No more data available over TCP"); |
1807 | 0 | dpp_connection_remove(conn); |
1808 | 0 | return; |
1809 | 0 | } |
1810 | 0 | wpa_printf(MSG_DEBUG, "DPP: Received %d octets", res); |
1811 | 0 | wpabuf_put(conn->msg, res); |
1812 | |
|
1813 | 0 | if (wpabuf_tailroom(conn->msg) > 0) { |
1814 | 0 | wpa_printf(MSG_DEBUG, |
1815 | 0 | "DPP: Need %u more octets of message payload", |
1816 | 0 | (unsigned int) wpabuf_tailroom(conn->msg)); |
1817 | 0 | return; |
1818 | 0 | } |
1819 | | |
1820 | 0 | conn->msg_len_octets = 0; |
1821 | 0 | wpa_hexdump_buf(MSG_DEBUG, "DPP: Received TCP message", conn->msg); |
1822 | 0 | if (wpabuf_len(conn->msg) < 1) { |
1823 | 0 | dpp_connection_remove(conn); |
1824 | 0 | return; |
1825 | 0 | } |
1826 | | |
1827 | 0 | pos = wpabuf_head(conn->msg); |
1828 | 0 | switch (*pos) { |
1829 | 0 | case WLAN_PA_VENDOR_SPECIFIC: |
1830 | 0 | if (dpp_controller_rx_action(conn, pos + 1, |
1831 | 0 | wpabuf_len(conn->msg) - 1) < 0) |
1832 | 0 | dpp_connection_remove(conn); |
1833 | 0 | break; |
1834 | 0 | case WLAN_PA_GAS_INITIAL_REQ: |
1835 | 0 | if (dpp_controller_rx_gas_req(conn, pos + 1, |
1836 | 0 | wpabuf_len(conn->msg) - 1) < 0) |
1837 | 0 | dpp_connection_remove(conn); |
1838 | 0 | break; |
1839 | 0 | case WLAN_PA_GAS_INITIAL_RESP: |
1840 | 0 | case WLAN_PA_GAS_COMEBACK_RESP: |
1841 | 0 | if (dpp_rx_gas_resp(conn, pos + 1, |
1842 | 0 | wpabuf_len(conn->msg) - 1, |
1843 | 0 | *pos == WLAN_PA_GAS_COMEBACK_RESP) < 0) |
1844 | 0 | dpp_connection_remove(conn); |
1845 | 0 | break; |
1846 | 0 | case WLAN_PA_GAS_COMEBACK_REQ: |
1847 | 0 | if (dpp_controller_rx_gas_comeback_req( |
1848 | 0 | conn, pos + 1, wpabuf_len(conn->msg) - 1) < 0) |
1849 | 0 | dpp_connection_remove(conn); |
1850 | 0 | break; |
1851 | 0 | default: |
1852 | 0 | wpa_printf(MSG_DEBUG, "DPP: Ignore unsupported message type %u", |
1853 | 0 | *pos); |
1854 | 0 | break; |
1855 | 0 | } |
1856 | 0 | } |
1857 | | |
1858 | | |
1859 | | static void dpp_controller_tcp_cb(int sd, void *eloop_ctx, void *sock_ctx) |
1860 | 0 | { |
1861 | 0 | struct dpp_controller *ctrl = eloop_ctx; |
1862 | 0 | struct sockaddr_in addr; |
1863 | 0 | socklen_t addr_len = sizeof(addr); |
1864 | 0 | int fd; |
1865 | 0 | struct dpp_connection *conn; |
1866 | |
|
1867 | 0 | wpa_printf(MSG_DEBUG, "DPP: New TCP connection"); |
1868 | |
|
1869 | 0 | fd = accept(ctrl->sock, (struct sockaddr *) &addr, &addr_len); |
1870 | 0 | if (fd < 0) { |
1871 | 0 | wpa_printf(MSG_DEBUG, |
1872 | 0 | "DPP: Failed to accept new connection: %s", |
1873 | 0 | strerror(errno)); |
1874 | 0 | return; |
1875 | 0 | } |
1876 | 0 | wpa_printf(MSG_DEBUG, "DPP: Connection from %s:%d", |
1877 | 0 | inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); |
1878 | |
|
1879 | 0 | conn = os_zalloc(sizeof(*conn)); |
1880 | 0 | if (!conn) |
1881 | 0 | goto fail; |
1882 | | |
1883 | 0 | conn->global = ctrl->global; |
1884 | 0 | conn->ctrl = ctrl; |
1885 | 0 | conn->msg_ctx = ctrl->msg_ctx; |
1886 | 0 | conn->cb_ctx = ctrl->cb_ctx; |
1887 | 0 | conn->process_conf_obj = ctrl->process_conf_obj; |
1888 | 0 | conn->tcp_msg_sent = ctrl->tcp_msg_sent; |
1889 | 0 | conn->sock = fd; |
1890 | 0 | conn->netrole = ctrl->netrole; |
1891 | |
|
1892 | 0 | if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) { |
1893 | 0 | wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s", |
1894 | 0 | strerror(errno)); |
1895 | 0 | goto fail; |
1896 | 0 | } |
1897 | | |
1898 | 0 | if (eloop_register_sock(conn->sock, EVENT_TYPE_READ, |
1899 | 0 | dpp_controller_rx, conn, NULL) < 0) |
1900 | 0 | goto fail; |
1901 | 0 | conn->read_eloop = 1; |
1902 | | |
1903 | | /* TODO: eloop timeout to expire connections that do not complete in |
1904 | | * reasonable time */ |
1905 | 0 | dl_list_add(&ctrl->conn, &conn->list); |
1906 | 0 | return; |
1907 | | |
1908 | 0 | fail: |
1909 | 0 | close(fd); |
1910 | 0 | os_free(conn); |
1911 | 0 | } |
1912 | | |
1913 | | |
1914 | | int dpp_tcp_pkex_init(struct dpp_global *dpp, struct dpp_pkex *pkex, |
1915 | | const struct hostapd_ip_addr *addr, int port, |
1916 | | void *msg_ctx, void *cb_ctx, |
1917 | | int (*pkex_done)(void *ctx, void *conn, |
1918 | | struct dpp_bootstrap_info *bi)) |
1919 | 0 | { |
1920 | 0 | struct dpp_connection *conn; |
1921 | 0 | struct sockaddr_storage saddr; |
1922 | 0 | socklen_t addrlen; |
1923 | 0 | const u8 *hdr, *pos, *end; |
1924 | 0 | char txt[100]; |
1925 | |
|
1926 | 0 | wpa_printf(MSG_DEBUG, "DPP: Initialize TCP connection to %s port %d", |
1927 | 0 | hostapd_ip_txt(addr, txt, sizeof(txt)), port); |
1928 | 0 | if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &saddr, &addrlen, |
1929 | 0 | addr, port) < 0) { |
1930 | 0 | dpp_pkex_free(pkex); |
1931 | 0 | return -1; |
1932 | 0 | } |
1933 | | |
1934 | 0 | conn = os_zalloc(sizeof(*conn)); |
1935 | 0 | if (!conn) { |
1936 | 0 | dpp_pkex_free(pkex); |
1937 | 0 | return -1; |
1938 | 0 | } |
1939 | | |
1940 | 0 | conn->msg_ctx = msg_ctx; |
1941 | 0 | conn->cb_ctx = cb_ctx; |
1942 | 0 | conn->pkex_done = pkex_done; |
1943 | 0 | conn->global = dpp; |
1944 | 0 | conn->pkex = pkex; |
1945 | 0 | conn->sock = socket(AF_INET, SOCK_STREAM, 0); |
1946 | 0 | if (conn->sock < 0) |
1947 | 0 | goto fail; |
1948 | | |
1949 | 0 | if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) { |
1950 | 0 | wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s", |
1951 | 0 | strerror(errno)); |
1952 | 0 | goto fail; |
1953 | 0 | } |
1954 | | |
1955 | 0 | if (connect(conn->sock, (struct sockaddr *) &saddr, addrlen) < 0) { |
1956 | 0 | if (errno != EINPROGRESS) { |
1957 | 0 | wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s", |
1958 | 0 | strerror(errno)); |
1959 | 0 | goto fail; |
1960 | 0 | } |
1961 | | |
1962 | | /* |
1963 | | * Continue connecting in the background; eloop will call us |
1964 | | * once the connection is ready (or failed). |
1965 | | */ |
1966 | 0 | } |
1967 | | |
1968 | 0 | if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, |
1969 | 0 | dpp_conn_tx_ready, conn, NULL) < 0) |
1970 | 0 | goto fail; |
1971 | 0 | conn->write_eloop = 1; |
1972 | |
|
1973 | 0 | hdr = wpabuf_head(pkex->exchange_req); |
1974 | 0 | end = hdr + wpabuf_len(pkex->exchange_req); |
1975 | 0 | hdr += 2; /* skip Category and Actiom */ |
1976 | 0 | pos = hdr + DPP_HDR_LEN; |
1977 | 0 | conn->msg_out = dpp_tcp_encaps(hdr, pos, end - pos); |
1978 | 0 | if (!conn->msg_out) |
1979 | 0 | goto fail; |
1980 | | /* Message will be sent in dpp_conn_tx_ready() */ |
1981 | | |
1982 | | /* TODO: eloop timeout to clear a connection if it does not complete |
1983 | | * properly */ |
1984 | 0 | dl_list_add(&dpp->tcp_init, &conn->list); |
1985 | 0 | return 0; |
1986 | 0 | fail: |
1987 | 0 | dpp_connection_free(conn); |
1988 | 0 | return -1; |
1989 | 0 | } |
1990 | | |
1991 | | |
1992 | | static int dpp_tcp_auth_start(struct dpp_connection *conn, |
1993 | | struct dpp_authentication *auth) |
1994 | 0 | { |
1995 | 0 | const u8 *hdr, *pos, *end; |
1996 | |
|
1997 | 0 | hdr = wpabuf_head(auth->req_msg); |
1998 | 0 | end = hdr + wpabuf_len(auth->req_msg); |
1999 | 0 | hdr += 2; /* skip Category and Actiom */ |
2000 | 0 | pos = hdr + DPP_HDR_LEN; |
2001 | 0 | conn->msg_out = dpp_tcp_encaps(hdr, pos, end - pos); |
2002 | 0 | if (!conn->msg_out) |
2003 | 0 | return -1; |
2004 | | /* Message will be sent in dpp_conn_tx_ready() */ |
2005 | 0 | return 0; |
2006 | 0 | } |
2007 | | |
2008 | | |
2009 | | int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth, |
2010 | | const struct hostapd_ip_addr *addr, int port, const char *name, |
2011 | | enum dpp_netrole netrole, const char *mud_url, |
2012 | | const char *extra_conf_req_name, |
2013 | | const char *extra_conf_req_value, |
2014 | | void *msg_ctx, void *cb_ctx, |
2015 | | int (*process_conf_obj)(void *ctx, |
2016 | | struct dpp_authentication *auth), |
2017 | | bool (*tcp_msg_sent)(void *ctx, |
2018 | | struct dpp_authentication *auth)) |
2019 | 0 | { |
2020 | 0 | struct dpp_connection *conn; |
2021 | 0 | struct sockaddr_storage saddr; |
2022 | 0 | socklen_t addrlen; |
2023 | 0 | char txt[100]; |
2024 | |
|
2025 | 0 | wpa_printf(MSG_DEBUG, "DPP: Initialize TCP connection to %s port %d", |
2026 | 0 | hostapd_ip_txt(addr, txt, sizeof(txt)), port); |
2027 | 0 | if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &saddr, &addrlen, |
2028 | 0 | addr, port) < 0) { |
2029 | 0 | dpp_auth_deinit(auth); |
2030 | 0 | return -1; |
2031 | 0 | } |
2032 | | |
2033 | 0 | conn = os_zalloc(sizeof(*conn)); |
2034 | 0 | if (!conn) { |
2035 | 0 | dpp_auth_deinit(auth); |
2036 | 0 | return -1; |
2037 | 0 | } |
2038 | | |
2039 | 0 | conn->msg_ctx = msg_ctx; |
2040 | 0 | conn->cb_ctx = cb_ctx; |
2041 | 0 | conn->process_conf_obj = process_conf_obj; |
2042 | 0 | conn->tcp_msg_sent = tcp_msg_sent; |
2043 | 0 | conn->name = os_strdup(name ? name : "Test"); |
2044 | 0 | if (mud_url) |
2045 | 0 | conn->mud_url = os_strdup(mud_url); |
2046 | 0 | if (extra_conf_req_name) |
2047 | 0 | conn->extra_conf_req_name = os_strdup(extra_conf_req_name); |
2048 | 0 | if (extra_conf_req_value) |
2049 | 0 | conn->extra_conf_req_value = os_strdup(extra_conf_req_value); |
2050 | 0 | conn->netrole = netrole; |
2051 | 0 | conn->global = dpp; |
2052 | 0 | conn->auth = auth; |
2053 | 0 | conn->sock = socket(AF_INET, SOCK_STREAM, 0); |
2054 | 0 | if (conn->sock < 0) |
2055 | 0 | goto fail; |
2056 | | |
2057 | 0 | if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) { |
2058 | 0 | wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s", |
2059 | 0 | strerror(errno)); |
2060 | 0 | goto fail; |
2061 | 0 | } |
2062 | | |
2063 | 0 | if (connect(conn->sock, (struct sockaddr *) &saddr, addrlen) < 0) { |
2064 | 0 | if (errno != EINPROGRESS) { |
2065 | 0 | wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s", |
2066 | 0 | strerror(errno)); |
2067 | 0 | goto fail; |
2068 | 0 | } |
2069 | | |
2070 | | /* |
2071 | | * Continue connecting in the background; eloop will call us |
2072 | | * once the connection is ready (or failed). |
2073 | | */ |
2074 | 0 | } |
2075 | | |
2076 | 0 | if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, |
2077 | 0 | dpp_conn_tx_ready, conn, NULL) < 0) |
2078 | 0 | goto fail; |
2079 | 0 | conn->write_eloop = 1; |
2080 | |
|
2081 | 0 | if (dpp_tcp_auth_start(conn, auth) < 0) |
2082 | 0 | goto fail; |
2083 | | |
2084 | | /* TODO: eloop timeout to clear a connection if it does not complete |
2085 | | * properly */ |
2086 | 0 | dl_list_add(&dpp->tcp_init, &conn->list); |
2087 | 0 | return 0; |
2088 | 0 | fail: |
2089 | 0 | dpp_connection_free(conn); |
2090 | 0 | return -1; |
2091 | 0 | } |
2092 | | |
2093 | | |
2094 | | int dpp_tcp_auth(struct dpp_global *dpp, void *_conn, |
2095 | | struct dpp_authentication *auth, const char *name, |
2096 | | enum dpp_netrole netrole, const char *mud_url, |
2097 | | const char *extra_conf_req_name, |
2098 | | const char *extra_conf_req_value, |
2099 | | int (*process_conf_obj)(void *ctx, |
2100 | | struct dpp_authentication *auth), |
2101 | | bool (*tcp_msg_sent)(void *ctx, |
2102 | | struct dpp_authentication *auth)) |
2103 | 0 | { |
2104 | 0 | struct dpp_connection *conn = _conn; |
2105 | | |
2106 | | /* Continue with Authentication exchange on an existing TCP connection. |
2107 | | */ |
2108 | 0 | conn->process_conf_obj = process_conf_obj; |
2109 | 0 | conn->tcp_msg_sent = tcp_msg_sent; |
2110 | 0 | os_free(conn->name); |
2111 | 0 | conn->name = os_strdup(name ? name : "Test"); |
2112 | 0 | os_free(conn->mud_url); |
2113 | 0 | conn->mud_url = mud_url ? os_strdup(mud_url) : NULL; |
2114 | 0 | os_free(conn->extra_conf_req_name); |
2115 | 0 | conn->extra_conf_req_name = extra_conf_req_name ? |
2116 | 0 | os_strdup(extra_conf_req_name) : NULL; |
2117 | 0 | conn->extra_conf_req_value = extra_conf_req_value ? |
2118 | 0 | os_strdup(extra_conf_req_value) : NULL; |
2119 | 0 | conn->netrole = netrole; |
2120 | 0 | conn->auth = auth; |
2121 | |
|
2122 | 0 | if (dpp_tcp_auth_start(conn, auth) < 0) |
2123 | 0 | return -1; |
2124 | | |
2125 | 0 | dpp_conn_tx_ready(conn->sock, conn, NULL); |
2126 | 0 | return 0; |
2127 | 0 | } |
2128 | | |
2129 | | |
2130 | | int dpp_controller_start(struct dpp_global *dpp, |
2131 | | struct dpp_controller_config *config) |
2132 | 0 | { |
2133 | 0 | struct dpp_controller *ctrl; |
2134 | 0 | int on = 1; |
2135 | 0 | struct sockaddr_in sin; |
2136 | 0 | int port; |
2137 | |
|
2138 | 0 | if (!dpp || dpp->controller) |
2139 | 0 | return -1; |
2140 | | |
2141 | 0 | ctrl = os_zalloc(sizeof(*ctrl)); |
2142 | 0 | if (!ctrl) |
2143 | 0 | return -1; |
2144 | 0 | ctrl->global = dpp; |
2145 | 0 | if (config->configurator_params) |
2146 | 0 | ctrl->configurator_params = |
2147 | 0 | os_strdup(config->configurator_params); |
2148 | 0 | dl_list_init(&ctrl->conn); |
2149 | 0 | ctrl->allowed_roles = config->allowed_roles; |
2150 | 0 | ctrl->qr_mutual = config->qr_mutual; |
2151 | 0 | ctrl->netrole = config->netrole; |
2152 | 0 | ctrl->msg_ctx = config->msg_ctx; |
2153 | 0 | ctrl->cb_ctx = config->cb_ctx; |
2154 | 0 | ctrl->process_conf_obj = config->process_conf_obj; |
2155 | 0 | ctrl->tcp_msg_sent = config->tcp_msg_sent; |
2156 | |
|
2157 | 0 | ctrl->sock = socket(AF_INET, SOCK_STREAM, 0); |
2158 | 0 | if (ctrl->sock < 0) |
2159 | 0 | goto fail; |
2160 | | |
2161 | 0 | if (setsockopt(ctrl->sock, SOL_SOCKET, SO_REUSEADDR, |
2162 | 0 | &on, sizeof(on)) < 0) { |
2163 | 0 | wpa_printf(MSG_DEBUG, |
2164 | 0 | "DPP: setsockopt(SO_REUSEADDR) failed: %s", |
2165 | 0 | strerror(errno)); |
2166 | | /* try to continue anyway */ |
2167 | 0 | } |
2168 | |
|
2169 | 0 | if (fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0) { |
2170 | 0 | wpa_printf(MSG_INFO, "DPP: fnctl(O_NONBLOCK) failed: %s", |
2171 | 0 | strerror(errno)); |
2172 | 0 | goto fail; |
2173 | 0 | } |
2174 | | |
2175 | | /* TODO: IPv6 */ |
2176 | 0 | os_memset(&sin, 0, sizeof(sin)); |
2177 | 0 | sin.sin_family = AF_INET; |
2178 | 0 | sin.sin_addr.s_addr = INADDR_ANY; |
2179 | 0 | port = config->tcp_port ? config->tcp_port : DPP_TCP_PORT; |
2180 | 0 | sin.sin_port = htons(port); |
2181 | 0 | if (bind(ctrl->sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { |
2182 | 0 | wpa_printf(MSG_INFO, |
2183 | 0 | "DPP: Failed to bind Controller TCP port: %s", |
2184 | 0 | strerror(errno)); |
2185 | 0 | goto fail; |
2186 | 0 | } |
2187 | 0 | if (listen(ctrl->sock, 10 /* max backlog */) < 0 || |
2188 | 0 | fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0 || |
2189 | 0 | eloop_register_sock(ctrl->sock, EVENT_TYPE_READ, |
2190 | 0 | dpp_controller_tcp_cb, ctrl, NULL)) |
2191 | 0 | goto fail; |
2192 | | |
2193 | 0 | dpp->controller = ctrl; |
2194 | 0 | wpa_printf(MSG_DEBUG, "DPP: Controller started on TCP port %d", port); |
2195 | 0 | return 0; |
2196 | 0 | fail: |
2197 | 0 | dpp_controller_free(ctrl); |
2198 | 0 | return -1; |
2199 | 0 | } |
2200 | | |
2201 | | |
2202 | | int dpp_controller_set_params(struct dpp_global *dpp, |
2203 | | const char *configurator_params) |
2204 | 0 | { |
2205 | |
|
2206 | 0 | if (!dpp || !dpp->controller) |
2207 | 0 | return -1; |
2208 | | |
2209 | 0 | if (configurator_params) { |
2210 | 0 | char *val = os_strdup(configurator_params); |
2211 | |
|
2212 | 0 | if (!val) |
2213 | 0 | return -1; |
2214 | 0 | os_free(dpp->controller->configurator_params); |
2215 | 0 | dpp->controller->configurator_params = val; |
2216 | 0 | } else { |
2217 | 0 | os_free(dpp->controller->configurator_params); |
2218 | 0 | dpp->controller->configurator_params = NULL; |
2219 | 0 | } |
2220 | | |
2221 | 0 | return 0; |
2222 | 0 | } |
2223 | | |
2224 | | |
2225 | | void dpp_controller_stop(struct dpp_global *dpp) |
2226 | 1.66k | { |
2227 | 1.66k | if (dpp) { |
2228 | 1.66k | dpp_controller_free(dpp->controller); |
2229 | 1.66k | dpp->controller = NULL; |
2230 | 1.66k | } |
2231 | 1.66k | } |
2232 | | |
2233 | | |
2234 | | void dpp_controller_stop_for_ctx(struct dpp_global *dpp, void *cb_ctx) |
2235 | 0 | { |
2236 | 0 | if (dpp && dpp->controller && dpp->controller->cb_ctx == cb_ctx) |
2237 | 0 | dpp_controller_stop(dpp); |
2238 | 0 | } |
2239 | | |
2240 | | |
2241 | | static bool dpp_tcp_peer_id_match(struct dpp_authentication *auth, |
2242 | | unsigned int id) |
2243 | 0 | { |
2244 | 0 | return auth && |
2245 | 0 | ((auth->peer_bi && auth->peer_bi->id == id) || |
2246 | 0 | (auth->tmp_peer_bi && auth->tmp_peer_bi->id == id)); |
2247 | 0 | } |
2248 | | |
2249 | | |
2250 | | static struct dpp_authentication * dpp_tcp_get_auth(struct dpp_global *dpp, |
2251 | | unsigned int id) |
2252 | 0 | { |
2253 | 0 | struct dpp_connection *conn; |
2254 | |
|
2255 | 0 | dl_list_for_each(conn, &dpp->tcp_init, struct dpp_connection, list) { |
2256 | 0 | if (dpp_tcp_peer_id_match(conn->auth, id)) |
2257 | 0 | return conn->auth; |
2258 | 0 | } |
2259 | | |
2260 | 0 | return NULL; |
2261 | 0 | } |
2262 | | |
2263 | | |
2264 | | struct dpp_authentication * dpp_controller_get_auth(struct dpp_global *dpp, |
2265 | | unsigned int id) |
2266 | 0 | { |
2267 | 0 | struct dpp_controller *ctrl = dpp->controller; |
2268 | 0 | struct dpp_connection *conn; |
2269 | |
|
2270 | 0 | if (!ctrl) |
2271 | 0 | return dpp_tcp_get_auth(dpp, id); |
2272 | | |
2273 | 0 | dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) { |
2274 | 0 | if (dpp_tcp_peer_id_match(conn->auth, id)) |
2275 | 0 | return conn->auth; |
2276 | 0 | } |
2277 | | |
2278 | 0 | return dpp_tcp_get_auth(dpp, id); |
2279 | 0 | } |
2280 | | |
2281 | | |
2282 | | void dpp_controller_new_qr_code(struct dpp_global *dpp, |
2283 | | struct dpp_bootstrap_info *bi) |
2284 | 0 | { |
2285 | 0 | struct dpp_controller *ctrl = dpp->controller; |
2286 | 0 | struct dpp_connection *conn; |
2287 | |
|
2288 | 0 | if (!ctrl) |
2289 | 0 | return; |
2290 | | |
2291 | 0 | dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) { |
2292 | 0 | struct dpp_authentication *auth = conn->auth; |
2293 | |
|
2294 | 0 | if (!auth->response_pending || |
2295 | 0 | dpp_notify_new_qr_code(auth, bi) != 1) |
2296 | 0 | continue; |
2297 | 0 | wpa_printf(MSG_DEBUG, |
2298 | 0 | "DPP: Sending out pending authentication response"); |
2299 | 0 | dpp_tcp_send_msg(conn, conn->auth->resp_msg); |
2300 | 0 | } |
2301 | 0 | } |
2302 | | |
2303 | | |
2304 | | void dpp_controller_pkex_add(struct dpp_global *dpp, |
2305 | | struct dpp_bootstrap_info *bi, |
2306 | | const char *code, const char *identifier) |
2307 | 0 | { |
2308 | 0 | struct dpp_controller *ctrl = dpp->controller; |
2309 | |
|
2310 | 0 | if (!ctrl) |
2311 | 0 | return; |
2312 | | |
2313 | 0 | ctrl->pkex_bi = bi; |
2314 | 0 | os_free(ctrl->pkex_code); |
2315 | 0 | ctrl->pkex_code = code ? os_strdup(code) : NULL; |
2316 | 0 | os_free(ctrl->pkex_identifier); |
2317 | 0 | ctrl->pkex_identifier = identifier ? os_strdup(identifier) : NULL; |
2318 | 0 | } |
2319 | | |
2320 | | |
2321 | | bool dpp_controller_is_own_pkex_req(struct dpp_global *dpp, |
2322 | | const u8 *buf, size_t len) |
2323 | 0 | { |
2324 | 0 | struct dpp_connection *conn; |
2325 | 0 | const u8 *attr_key = NULL; |
2326 | 0 | u16 attr_key_len = 0; |
2327 | |
|
2328 | 0 | dl_list_for_each(conn, &dpp->tcp_init, struct dpp_connection, list) { |
2329 | 0 | if (!conn->pkex || !conn->pkex->enc_key) |
2330 | 0 | continue; |
2331 | | |
2332 | 0 | if (!attr_key) { |
2333 | 0 | attr_key = dpp_get_attr(buf, len, |
2334 | 0 | DPP_ATTR_ENCRYPTED_KEY, |
2335 | 0 | &attr_key_len); |
2336 | 0 | if (!attr_key) |
2337 | 0 | return false; |
2338 | 0 | } |
2339 | | |
2340 | 0 | if (attr_key_len == wpabuf_len(conn->pkex->enc_key) && |
2341 | 0 | os_memcmp(attr_key, wpabuf_head(conn->pkex->enc_key), |
2342 | 0 | attr_key_len) == 0) |
2343 | 0 | return true; |
2344 | 0 | } |
2345 | | |
2346 | 0 | return false; |
2347 | 0 | } |
2348 | | |
2349 | | |
2350 | | void dpp_tcp_init_flush(struct dpp_global *dpp) |
2351 | 1.66k | { |
2352 | 1.66k | struct dpp_connection *conn, *tmp; |
2353 | | |
2354 | 1.66k | dl_list_for_each_safe(conn, tmp, &dpp->tcp_init, struct dpp_connection, |
2355 | 1.66k | list) |
2356 | 0 | dpp_connection_remove(conn); |
2357 | 1.66k | } |
2358 | | |
2359 | | |
2360 | | static void dpp_relay_controller_free(struct dpp_relay_controller *ctrl) |
2361 | 0 | { |
2362 | 0 | struct dpp_connection *conn, *tmp; |
2363 | 0 | char txt[100]; |
2364 | |
|
2365 | 0 | wpa_printf(MSG_DEBUG, "DPP: Remove Relay connection to Controller %s", |
2366 | 0 | hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt))); |
2367 | |
|
2368 | 0 | dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection, |
2369 | 0 | list) |
2370 | 0 | dpp_connection_remove(conn); |
2371 | 0 | os_free(ctrl); |
2372 | 0 | } |
2373 | | |
2374 | | |
2375 | | void dpp_relay_flush_controllers(struct dpp_global *dpp) |
2376 | 1.66k | { |
2377 | 1.66k | struct dpp_relay_controller *ctrl, *tmp; |
2378 | | |
2379 | 1.66k | if (!dpp) |
2380 | 0 | return; |
2381 | | |
2382 | 1.66k | dl_list_for_each_safe(ctrl, tmp, &dpp->controllers, |
2383 | 1.66k | struct dpp_relay_controller, list) { |
2384 | 0 | dl_list_del(&ctrl->list); |
2385 | 0 | dpp_relay_controller_free(ctrl); |
2386 | 0 | } |
2387 | | |
2388 | 1.66k | if (dpp->tmp_controller) { |
2389 | 0 | dpp_relay_controller_free(dpp->tmp_controller); |
2390 | 0 | dpp->tmp_controller = NULL; |
2391 | 0 | } |
2392 | 1.66k | } |
2393 | | |
2394 | | |
2395 | | void dpp_relay_remove_controller(struct dpp_global *dpp, |
2396 | | const struct hostapd_ip_addr *addr) |
2397 | 0 | { |
2398 | 0 | struct dpp_relay_controller *ctrl; |
2399 | |
|
2400 | 0 | if (!dpp) |
2401 | 0 | return; |
2402 | | |
2403 | 0 | dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller, |
2404 | 0 | list) { |
2405 | 0 | if (hostapd_ip_equal(&ctrl->ipaddr, addr)) { |
2406 | 0 | dl_list_del(&ctrl->list); |
2407 | 0 | dpp_relay_controller_free(ctrl); |
2408 | 0 | return; |
2409 | 0 | } |
2410 | 0 | } |
2411 | | |
2412 | 0 | if (dpp->tmp_controller && |
2413 | 0 | hostapd_ip_equal(&dpp->tmp_controller->ipaddr, addr)) { |
2414 | 0 | dpp_relay_controller_free(dpp->tmp_controller); |
2415 | 0 | dpp->tmp_controller = NULL; |
2416 | 0 | } |
2417 | 0 | } |
2418 | | |
2419 | | |
2420 | | static void dpp_relay_tcp_cb(int sd, void *eloop_ctx, void *sock_ctx) |
2421 | 0 | { |
2422 | 0 | struct dpp_global *dpp = eloop_ctx; |
2423 | 0 | struct sockaddr_in addr; |
2424 | 0 | socklen_t addr_len = sizeof(addr); |
2425 | 0 | int fd; |
2426 | 0 | struct dpp_relay_controller *ctrl; |
2427 | 0 | struct dpp_connection *conn = NULL; |
2428 | |
|
2429 | 0 | wpa_printf(MSG_DEBUG, "DPP: New TCP connection (Relay)"); |
2430 | |
|
2431 | 0 | fd = accept(dpp->relay_sock, (struct sockaddr *) &addr, &addr_len); |
2432 | 0 | if (fd < 0) { |
2433 | 0 | wpa_printf(MSG_DEBUG, |
2434 | 0 | "DPP: Failed to accept new connection: %s", |
2435 | 0 | strerror(errno)); |
2436 | 0 | return; |
2437 | 0 | } |
2438 | 0 | wpa_printf(MSG_DEBUG, "DPP: Connection from %s:%d", |
2439 | 0 | inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); |
2440 | |
|
2441 | 0 | ctrl = dpp_relay_controller_get_addr(dpp, &addr); |
2442 | 0 | if (!ctrl && dpp->tmp_controller && |
2443 | 0 | dl_list_len(&dpp->tmp_controller->conn)) { |
2444 | 0 | char txt[100]; |
2445 | |
|
2446 | 0 | wpa_printf(MSG_DEBUG, |
2447 | 0 | "DPP: Remove a temporaty Controller entry for %s", |
2448 | 0 | hostapd_ip_txt(&dpp->tmp_controller->ipaddr, |
2449 | 0 | txt, sizeof(txt))); |
2450 | 0 | dpp_relay_controller_free(dpp->tmp_controller); |
2451 | 0 | dpp->tmp_controller = NULL; |
2452 | 0 | } |
2453 | 0 | if (!ctrl && !dpp->tmp_controller) { |
2454 | 0 | wpa_printf(MSG_DEBUG, "DPP: Add a temporary Controller entry"); |
2455 | 0 | ctrl = os_zalloc(sizeof(*ctrl)); |
2456 | 0 | if (!ctrl) |
2457 | 0 | goto fail; |
2458 | 0 | dl_list_init(&ctrl->conn); |
2459 | 0 | ctrl->global = dpp; |
2460 | 0 | ctrl->ipaddr.af = AF_INET; |
2461 | 0 | ctrl->ipaddr.u.v4.s_addr = addr.sin_addr.s_addr; |
2462 | 0 | ctrl->msg_ctx = dpp->relay_msg_ctx; |
2463 | 0 | ctrl->cb_ctx = dpp->relay_cb_ctx; |
2464 | 0 | ctrl->tx = dpp->relay_tx; |
2465 | 0 | ctrl->gas_resp_tx = dpp->relay_gas_resp_tx; |
2466 | 0 | dpp->tmp_controller = ctrl; |
2467 | 0 | } |
2468 | 0 | if (!ctrl) { |
2469 | 0 | wpa_printf(MSG_DEBUG, |
2470 | 0 | "DPP: No Controller found for that address"); |
2471 | 0 | goto fail; |
2472 | 0 | } |
2473 | | |
2474 | 0 | if (dl_list_len(&ctrl->conn) >= 15) { |
2475 | 0 | wpa_printf(MSG_DEBUG, |
2476 | 0 | "DPP: Too many ongoing Relay connections to the Controller - cannot start a new one"); |
2477 | 0 | goto fail; |
2478 | 0 | } |
2479 | | |
2480 | 0 | conn = os_zalloc(sizeof(*conn)); |
2481 | 0 | if (!conn) |
2482 | 0 | goto fail; |
2483 | | |
2484 | 0 | conn->global = ctrl->global; |
2485 | 0 | conn->relay = ctrl; |
2486 | 0 | conn->msg_ctx = ctrl->msg_ctx; |
2487 | 0 | conn->cb_ctx = ctrl->global->cb_ctx; |
2488 | 0 | os_memset(conn->mac_addr, 0xff, ETH_ALEN); |
2489 | 0 | conn->sock = fd; |
2490 | |
|
2491 | 0 | if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) { |
2492 | 0 | wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s", |
2493 | 0 | strerror(errno)); |
2494 | 0 | goto fail; |
2495 | 0 | } |
2496 | | |
2497 | 0 | if (eloop_register_sock(conn->sock, EVENT_TYPE_READ, |
2498 | 0 | dpp_controller_rx, conn, NULL) < 0) |
2499 | 0 | goto fail; |
2500 | 0 | conn->read_eloop = 1; |
2501 | | |
2502 | | /* TODO: eloop timeout to expire connections that do not complete in |
2503 | | * reasonable time */ |
2504 | 0 | dl_list_add(&ctrl->conn, &conn->list); |
2505 | 0 | return; |
2506 | | |
2507 | 0 | fail: |
2508 | 0 | close(fd); |
2509 | 0 | os_free(conn); |
2510 | 0 | } |
2511 | | |
2512 | | |
2513 | | int dpp_relay_listen(struct dpp_global *dpp, int port, |
2514 | | struct dpp_relay_config *config) |
2515 | 0 | { |
2516 | 0 | int s; |
2517 | 0 | int on = 1; |
2518 | 0 | struct sockaddr_in sin; |
2519 | |
|
2520 | 0 | if (dpp->relay_sock >= 0) { |
2521 | 0 | wpa_printf(MSG_INFO, "DPP: %s(%d) - relay port already opened", |
2522 | 0 | __func__, port); |
2523 | 0 | return -1; |
2524 | 0 | } |
2525 | | |
2526 | 0 | s = socket(AF_INET, SOCK_STREAM, 0); |
2527 | 0 | if (s < 0) { |
2528 | 0 | wpa_printf(MSG_INFO, |
2529 | 0 | "DPP: socket(SOCK_STREAM) failed: %s", |
2530 | 0 | strerror(errno)); |
2531 | 0 | return -1; |
2532 | 0 | } |
2533 | | |
2534 | 0 | if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { |
2535 | 0 | wpa_printf(MSG_DEBUG, |
2536 | 0 | "DPP: setsockopt(SO_REUSEADDR) failed: %s", |
2537 | 0 | strerror(errno)); |
2538 | | /* try to continue anyway */ |
2539 | 0 | } |
2540 | |
|
2541 | 0 | if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) { |
2542 | 0 | wpa_printf(MSG_INFO, "DPP: fnctl(O_NONBLOCK) failed: %s", |
2543 | 0 | strerror(errno)); |
2544 | 0 | close(s); |
2545 | 0 | return -1; |
2546 | 0 | } |
2547 | | |
2548 | | /* TODO: IPv6 */ |
2549 | 0 | os_memset(&sin, 0, sizeof(sin)); |
2550 | 0 | sin.sin_family = AF_INET; |
2551 | 0 | sin.sin_addr.s_addr = INADDR_ANY; |
2552 | 0 | sin.sin_port = htons(port); |
2553 | 0 | if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { |
2554 | 0 | wpa_printf(MSG_INFO, |
2555 | 0 | "DPP: Failed to bind Relay TCP port: %s", |
2556 | 0 | strerror(errno)); |
2557 | 0 | close(s); |
2558 | 0 | return -1; |
2559 | 0 | } |
2560 | 0 | if (listen(s, 10 /* max backlog */) < 0 || |
2561 | 0 | fcntl(s, F_SETFL, O_NONBLOCK) < 0 || |
2562 | 0 | eloop_register_sock(s, EVENT_TYPE_READ, dpp_relay_tcp_cb, dpp, |
2563 | 0 | NULL)) { |
2564 | 0 | close(s); |
2565 | 0 | return -1; |
2566 | 0 | } |
2567 | | |
2568 | 0 | dpp->relay_sock = s; |
2569 | 0 | dpp->relay_msg_ctx = config->msg_ctx; |
2570 | 0 | dpp->relay_cb_ctx = config->cb_ctx; |
2571 | 0 | dpp->relay_tx = config->tx; |
2572 | 0 | dpp->relay_gas_resp_tx = config->gas_resp_tx; |
2573 | 0 | wpa_printf(MSG_DEBUG, "DPP: Relay started on TCP port %d", port); |
2574 | 0 | return 0; |
2575 | 0 | } |
2576 | | |
2577 | | |
2578 | | void dpp_relay_stop_listen(struct dpp_global *dpp) |
2579 | 0 | { |
2580 | 0 | if (!dpp || dpp->relay_sock < 0) |
2581 | 0 | return; |
2582 | 0 | eloop_unregister_sock(dpp->relay_sock, EVENT_TYPE_READ); |
2583 | 0 | close(dpp->relay_sock); |
2584 | 0 | dpp->relay_sock = -1; |
2585 | 0 | } |
2586 | | |
2587 | | |
2588 | | bool dpp_tcp_conn_status_requested(struct dpp_global *dpp) |
2589 | 0 | { |
2590 | 0 | struct dpp_connection *conn; |
2591 | |
|
2592 | 0 | if (!dpp) |
2593 | 0 | return false; |
2594 | | |
2595 | 0 | dl_list_for_each(conn, &dpp->tcp_init, struct dpp_connection, list) { |
2596 | 0 | if (conn->auth && conn->auth->conn_status_requested) |
2597 | 0 | return true; |
2598 | 0 | } |
2599 | | |
2600 | 0 | return false; |
2601 | 0 | } |
2602 | | |
2603 | | |
2604 | | static void dpp_tcp_send_conn_status_msg(struct dpp_global *dpp, |
2605 | | struct dpp_connection *conn, |
2606 | | enum dpp_status_error result, |
2607 | | const u8 *ssid, size_t ssid_len, |
2608 | | const char *channel_list) |
2609 | 0 | { |
2610 | 0 | struct dpp_authentication *auth = conn->auth; |
2611 | 0 | int res; |
2612 | 0 | struct wpabuf *msg; |
2613 | 0 | struct dpp_connection *c; |
2614 | |
|
2615 | 0 | auth->conn_status_requested = 0; |
2616 | |
|
2617 | 0 | msg = dpp_build_conn_status_result(auth, result, ssid, ssid_len, |
2618 | 0 | channel_list); |
2619 | 0 | if (!msg) { |
2620 | 0 | dpp_connection_remove(conn); |
2621 | 0 | return; |
2622 | 0 | } |
2623 | | |
2624 | 0 | res = dpp_tcp_send_msg(conn, msg); |
2625 | 0 | wpabuf_free(msg); |
2626 | |
|
2627 | 0 | if (res < 0) { |
2628 | 0 | dpp_connection_remove(conn); |
2629 | 0 | return; |
2630 | 0 | } |
2631 | | |
2632 | | /* conn might have been removed during the dpp_tcp_send_msg() call, so |
2633 | | * need to check that it is still present before modifying it. */ |
2634 | 0 | dl_list_for_each(c, &dpp->tcp_init, struct dpp_connection, list) { |
2635 | 0 | if (conn == c) { |
2636 | | /* This exchange will be terminated in the TX status |
2637 | | * handler */ |
2638 | 0 | conn->on_tcp_tx_complete_remove = 1; |
2639 | 0 | break; |
2640 | 0 | } |
2641 | 0 | } |
2642 | 0 | } |
2643 | | |
2644 | | |
2645 | | void dpp_tcp_send_conn_status(struct dpp_global *dpp, |
2646 | | enum dpp_status_error result, |
2647 | | const u8 *ssid, size_t ssid_len, |
2648 | | const char *channel_list) |
2649 | 0 | { |
2650 | 0 | struct dpp_connection *conn; |
2651 | |
|
2652 | 0 | dl_list_for_each(conn, &dpp->tcp_init, struct dpp_connection, list) { |
2653 | 0 | if (conn->auth && conn->auth->conn_status_requested) { |
2654 | 0 | dpp_tcp_send_conn_status_msg(dpp, conn, result, ssid, |
2655 | 0 | ssid_len, channel_list); |
2656 | 0 | break; |
2657 | 0 | } |
2658 | 0 | } |
2659 | 0 | } |
2660 | | |
2661 | | #endif /* CONFIG_DPP2 */ |