diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c index c00da40f2..ce2ec5faa 100644 --- a/src/ap/dpp_hostapd.c +++ b/src/ap/dpp_hostapd.c @@ -503,8 +503,9 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) if (hapd->dpp_auth) dpp_auth_deinit(hapd->dpp_auth); + /* TODO: hw_modes */ hapd->dpp_auth = dpp_auth_init(hapd->msg_ctx, peer_bi, own_bi, - configurator, 0); + configurator, 0, NULL, 0); if (!hapd->dpp_auth) goto fail; hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth); @@ -513,10 +514,6 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd) /* TODO: Support iteration over all frequencies and filtering of * frequencies based on locally enabled channels that allow initiation * of transmission. */ - if (peer_bi->num_freq > 0) - hapd->dpp_auth->curr_freq = peer_bi->freq[0]; - else - hapd->dpp_auth->curr_freq = 2412; if (is_zero_ether_addr(peer_bi->mac_addr)) { dst = broadcast; diff --git a/src/common/dpp.c b/src/common/dpp.c index 12a154be9..564cbe731 100644 --- a/src/common/dpp.c +++ b/src/common/dpp.c @@ -22,6 +22,7 @@ #include "crypto/aes_siv.h" #include "crypto/sha384.h" #include "crypto/sha512.h" +#include "drivers/driver.h" #include "dpp.h" @@ -1701,11 +1702,171 @@ skip_wrapped_data: } +static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes, + u16 num_modes, unsigned int freq) +{ + u16 m; + int c, flag; + + if (!own_modes || !num_modes) + return 1; + + for (m = 0; m < num_modes; m++) { + for (c = 0; c < own_modes[m].num_channels; c++) { + if ((unsigned int) own_modes[m].channels[c].freq != + freq) + continue; + flag = own_modes[m].channels[c].flag; + if (!(flag & (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_NO_IR | + HOSTAPD_CHAN_RADAR))) + return 1; + } + } + + wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq); + return 0; +} + + +static int freq_included(const unsigned int freqs[], unsigned int num, + unsigned int freq) +{ + while (num > 0) { + if (freqs[--num] == freq) + return 1; + } + return 0; +} + + +static void freq_to_start(unsigned int freqs[], unsigned int num, + unsigned int freq) +{ + unsigned int i; + + for (i = 0; i < num; i++) { + if (freqs[i] == freq) + break; + } + if (i == 0 || i >= num) + return; + os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0])); + freqs[0] = freq; +} + + +static int dpp_channel_intersect(struct dpp_authentication *auth, + struct hostapd_hw_modes *own_modes, + u16 num_modes) +{ + struct dpp_bootstrap_info *peer_bi = auth->peer_bi; + unsigned int i, freq; + + for (i = 0; i < peer_bi->num_freq; i++) { + freq = peer_bi->freq[i]; + if (freq_included(auth->freq, auth->num_freq, freq)) + continue; + if (dpp_channel_ok_init(own_modes, num_modes, freq)) + auth->freq[auth->num_freq++] = freq; + } + if (!auth->num_freq) { + wpa_printf(MSG_INFO, + "DPP: No available channels for initiating DPP Authentication"); + return -1; + } + auth->curr_freq = auth->freq[0]; + return 0; +} + + +static int dpp_channel_local_list(struct dpp_authentication *auth, + struct hostapd_hw_modes *own_modes, + u16 num_modes) +{ + u16 m; + int c, flag; + unsigned int freq; + + auth->num_freq = 0; + + if (!own_modes || !num_modes) { + auth->freq[0] = 2412; + auth->freq[1] = 2437; + auth->freq[2] = 2462; + auth->num_freq = 3; + return 0; + } + + for (m = 0; m < num_modes; m++) { + for (c = 0; c < own_modes[m].num_channels; c++) { + freq = own_modes[m].channels[c].freq; + flag = own_modes[m].channels[c].flag; + if (flag & (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_NO_IR | + HOSTAPD_CHAN_RADAR)) + continue; + if (freq_included(auth->freq, auth->num_freq, freq)) + continue; + auth->freq[auth->num_freq++] = freq; + if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) { + m = num_modes; + break; + } + } + } + + return auth->num_freq == 0 ? -1 : 0; +} + + +static int dpp_prepare_channel_list(struct dpp_authentication *auth, + struct hostapd_hw_modes *own_modes, + u16 num_modes) +{ + int res; + char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end; + unsigned int i; + + if (auth->peer_bi->num_freq > 0) + res = dpp_channel_intersect(auth, own_modes, num_modes); + else + res = dpp_channel_local_list(auth, own_modes, num_modes); + if (res < 0) + return res; + + /* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most + * likely channels first. */ + freq_to_start(auth->freq, auth->num_freq, 2462); + freq_to_start(auth->freq, auth->num_freq, 2412); + freq_to_start(auth->freq, auth->num_freq, 2437); + + auth->freq_idx = 0; + auth->curr_freq = auth->freq[0]; + + pos = freqs; + end = pos + sizeof(freqs); + for (i = 0; i < auth->num_freq; i++) { + res = os_snprintf(pos, end - pos, " %u", auth->freq[i]); + if (os_snprintf_error(end - pos, res)) + break; + pos += res; + } + *pos = '\0'; + wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s", + freqs); + + return 0; +} + + struct dpp_authentication * dpp_auth_init(void *msg_ctx, struct dpp_bootstrap_info *peer_bi, struct dpp_bootstrap_info *own_bi, int configurator, - unsigned int neg_freq) + unsigned int neg_freq, + struct hostapd_hw_modes *own_modes, + u16 num_modes) { struct dpp_authentication *auth; size_t nonce_len; @@ -1720,11 +1881,15 @@ struct dpp_authentication * dpp_auth_init(void *msg_ctx, return NULL; auth->msg_ctx = msg_ctx; auth->initiator = 1; + auth->waiting_auth_resp = 1; auth->configurator = configurator; auth->peer_bi = peer_bi; auth->own_bi = own_bi; auth->curve = peer_bi->curve; + if (dpp_prepare_channel_list(auth, own_modes, num_modes) < 0) + goto fail; + nonce_len = auth->curve->nonce_len; if (random_get_bytes(auth->i_nonce, nonce_len)) { wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce"); @@ -2910,6 +3075,8 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, u8 r_auth2[DPP_MAX_HASH_LEN]; u8 role; + auth->waiting_auth_resp = 0; + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, &wrapped_data_len); if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { diff --git a/src/common/dpp.h b/src/common/dpp.h index d921dfbe2..091679a79 100644 --- a/src/common/dpp.h +++ b/src/common/dpp.h @@ -167,8 +167,13 @@ struct dpp_authentication { EVP_PKEY *peer_protocol_key; struct wpabuf *req_msg; struct wpabuf *resp_msg; + /* Intersection of possible frequencies for initiating DPP + * Authentication exchange */ + unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ]; + unsigned int num_freq, freq_idx; unsigned int curr_freq; unsigned int neg_freq; + unsigned int num_freq_iters; size_t secret_len; u8 Mx[DPP_MAX_SHARED_SECRET_LEN]; u8 Nx[DPP_MAX_SHARED_SECRET_LEN]; @@ -177,6 +182,7 @@ struct dpp_authentication { u8 k2[DPP_MAX_HASH_LEN]; u8 ke[DPP_MAX_HASH_LEN]; int initiator; + int waiting_auth_resp; int configurator; int remove_on_tx_status; int auth_success; @@ -298,11 +304,14 @@ int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info); struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri); char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve, const u8 *privkey, size_t privkey_len); +struct hostapd_hw_modes; struct dpp_authentication * dpp_auth_init(void *msg_ctx, struct dpp_bootstrap_info *peer_bi, struct dpp_bootstrap_info *own_bi, int configurator, - unsigned int neg_freq); + unsigned int neg_freq, + struct hostapd_hw_modes *own_modes, + u16 num_modes); struct dpp_authentication * dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, struct dpp_bootstrap_info *peer_bi, diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index b3fd287b8..f2a3c4bf9 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -153,6 +153,7 @@ extern "C" { /* DPP events */ #define DPP_EVENT_AUTH_SUCCESS "DPP-AUTH-SUCCESS " +#define DPP_EVENT_AUTH_INIT_FAILED "DPP-AUTH-INIT-FAILED " #define DPP_EVENT_NOT_COMPATIBLE "DPP-NOT-COMPATIBLE " #define DPP_EVENT_RESPONSE_PENDING "DPP-RESPONSE-PENDING " #define DPP_EVENT_SCAN_PEER_QR_CODE "DPP-SCAN-PEER-QR-CODE " diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 22696fa95..296423ae3 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -605,6 +605,12 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, } else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) { os_free(wpa_s->dpp_configurator_params); wpa_s->dpp_configurator_params = os_strdup(value); + } else if (os_strcasecmp(cmd, "dpp_init_max_tries") == 0) { + wpa_s->dpp_init_max_tries = atoi(value); + } else if (os_strcasecmp(cmd, "dpp_init_retry_time") == 0) { + wpa_s->dpp_init_retry_time = atoi(value); + } else if (os_strcasecmp(cmd, "dpp_resp_wait_time") == 0) { + wpa_s->dpp_resp_wait_time = atoi(value); #endif /* CONFIG_DPP */ #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { @@ -7746,6 +7752,9 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) #ifdef CONFIG_DPP wpas_dpp_deinit(wpa_s); + wpa_s->dpp_init_max_tries = 0; + wpa_s->dpp_init_retry_time = 0; + wpa_s->dpp_resp_wait_time = 0; #endif /* CONFIG_DPP */ #ifdef CONFIG_TDLS diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c index 95a1942f2..813a528b1 100644 --- a/wpa_supplicant/dpp_supplicant.c +++ b/wpa_supplicant/dpp_supplicant.c @@ -35,6 +35,8 @@ static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *bssid, const u8 *data, size_t data_len, enum offchannel_send_action_result result); +static void wpas_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx); +static int wpas_dpp_auth_init_next(struct wpa_supplicant *wpa_s); static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -305,6 +307,7 @@ static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s, enum offchannel_send_action_result result) { const char *res_txt; + struct dpp_authentication *auth = wpa_s->dpp_auth; res_txt = result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" : (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" : @@ -323,6 +326,7 @@ static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s, if (wpa_s->dpp_auth->remove_on_tx_status) { wpa_printf(MSG_DEBUG, "DPP: Terminate authentication exchange due to an earlier error"); + eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); offchannel_send_action_done(wpa_s); dpp_auth_deinit(wpa_s->dpp_auth); @@ -337,8 +341,13 @@ static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s, result != OFFCHANNEL_SEND_ACTION_SUCCESS) { wpa_printf(MSG_DEBUG, "DPP: Unicast DPP Action frame was not ACKed"); - /* TODO: In case of DPP Authentication Request frame, move to - * the next channel immediately */ + if (auth->waiting_auth_resp) { + /* In case of DPP Authentication Request frame, move to + * the next channel immediately. */ + offchannel_send_action_done(wpa_s); + wpas_dpp_auth_init_next(wpa_s); + return; + } } if (!wpa_s->dpp_auth_ok_on_ack && wpa_s->dpp_auth->neg_freq > 0 && @@ -357,9 +366,25 @@ static void wpas_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; unsigned int freq; + struct os_reltime now; if (!wpa_s->dpp_auth) return; + + if (wpa_s->dpp_auth->waiting_auth_resp) { + unsigned int wait_time; + + wait_time = wpa_s->dpp_resp_wait_time ? + wpa_s->dpp_resp_wait_time : 2; + os_get_reltime(&now); + if (os_reltime_expired(&now, &wpa_s->dpp_last_init, + wait_time)) { + offchannel_send_action_done(wpa_s); + wpas_dpp_auth_init_next(wpa_s); + return; + } + } + freq = wpa_s->dpp_auth->curr_freq; if (wpa_s->dpp_auth->neg_freq > 0) freq = wpa_s->dpp_auth->neg_freq; @@ -516,14 +541,90 @@ fail: } +static void wpas_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (!wpa_s->dpp_auth) + return; + wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout"); + wpas_dpp_auth_init_next(wpa_s); +} + + +static int wpas_dpp_auth_init_next(struct wpa_supplicant *wpa_s) +{ + struct dpp_authentication *auth = wpa_s->dpp_auth; + const u8 *dst; + unsigned int wait_time, freq, max_tries; + + if (!auth) + return -1; + if (auth->freq_idx >= auth->num_freq) { + auth->num_freq_iters++; + if (wpa_s->dpp_init_max_tries) + max_tries = wpa_s->dpp_init_max_tries; + else + max_tries = 5; + if (auth->num_freq_iters >= max_tries) { + wpa_printf(MSG_INFO, + "DPP: No response received from responder - stopping initiation attempt"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED); + eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, + wpa_s, NULL); + offchannel_send_action_done(wpa_s); + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; + return -1; + } + auth->freq_idx = 0; + eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL); + if (wpa_s->dpp_init_retry_time) + wait_time = wpa_s->dpp_init_retry_time; + else + wait_time = 10000; + eloop_register_timeout(wait_time / 1000, + (wait_time % 1000) * 1000, + wpas_dpp_init_timeout, wpa_s, + NULL); + return 0; + } + freq = auth->freq[auth->freq_idx++]; + auth->curr_freq = freq; + + if (is_zero_ether_addr(auth->peer_bi->mac_addr)) + dst = broadcast; + else + dst = auth->peer_bi->mac_addr; + wpa_s->dpp_auth_ok_on_ack = 0; + eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); + wait_time = wpa_s->max_remain_on_chan; + if (wait_time > 2000) + wait_time = 2000; + eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000, + wpas_dpp_reply_wait_timeout, + wpa_s, NULL); + if (auth->neg_freq > 0 && freq != auth->neg_freq) { + wpa_printf(MSG_DEBUG, + "DPP: Initiate on %u MHz and move to neg_freq %u MHz for response", + freq, auth->neg_freq); + } + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(dst), freq, DPP_PA_AUTHENTICATION_REQ); + os_get_reltime(&wpa_s->dpp_last_init); + return offchannel_send_action(wpa_s, freq, dst, + wpa_s->own_addr, broadcast, + wpabuf_head(auth->req_msg), + wpabuf_len(auth->req_msg), + wait_time, wpas_dpp_tx_status, 0); +} + + int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd) { const char *pos; struct dpp_bootstrap_info *peer_bi, *own_bi = NULL; - const u8 *dst; - int res; int configurator = 1; - unsigned int wait_time; unsigned int neg_freq = 0; wpa_s->dpp_gas_client = 0; @@ -579,57 +680,26 @@ int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd) neg_freq = atoi(pos + 10); if (wpa_s->dpp_auth) { + eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); offchannel_send_action_done(wpa_s); dpp_auth_deinit(wpa_s->dpp_auth); } wpa_s->dpp_auth = dpp_auth_init(wpa_s, peer_bi, own_bi, configurator, - neg_freq); + neg_freq, + wpa_s->hw.modes, wpa_s->hw.num_modes); if (!wpa_s->dpp_auth) goto fail; wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth); wpas_dpp_set_configurator(wpa_s, wpa_s->dpp_auth, cmd); - /* TODO: Support iteration over all frequencies and filtering of - * frequencies based on locally enabled channels that allow initiation - * of transmission. */ - if (peer_bi->num_freq > 0) - wpa_s->dpp_auth->curr_freq = peer_bi->freq[0]; - else - wpa_s->dpp_auth->curr_freq = 2412; wpa_s->dpp_auth->neg_freq = neg_freq; - if (is_zero_ether_addr(peer_bi->mac_addr)) { - dst = broadcast; - } else { - dst = peer_bi->mac_addr; + if (!is_zero_ether_addr(peer_bi->mac_addr)) os_memcpy(wpa_s->dpp_auth->peer_mac_addr, peer_bi->mac_addr, ETH_ALEN); - } - wpa_s->dpp_auth_ok_on_ack = 0; - eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); - wait_time = wpa_s->max_remain_on_chan; - if (wait_time > 2000) - wait_time = 2000; - eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000, - wpas_dpp_reply_wait_timeout, - wpa_s, NULL); - if (neg_freq > 0 && wpa_s->dpp_auth->curr_freq != neg_freq) { - wpa_printf(MSG_DEBUG, - "DPP: Initiate on curr_freq %u MHz and move to neg_freq %u MHz for response", - wpa_s->dpp_auth->curr_freq, - wpa_s->dpp_auth->neg_freq); - } - wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", - MAC2STR(dst), wpa_s->dpp_auth->curr_freq, - DPP_PA_AUTHENTICATION_REQ); - res = offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq, - dst, wpa_s->own_addr, broadcast, - wpabuf_head(wpa_s->dpp_auth->req_msg), - wpabuf_len(wpa_s->dpp_auth->req_msg), - wait_time, wpas_dpp_tx_status, 0); - return res; + return wpas_dpp_auth_init_next(wpa_s); fail: return -1; } @@ -1228,6 +1298,7 @@ static void wpas_dpp_rx_auth_resp(struct wpa_supplicant *wpa_s, const u8 *src, auth->curr_freq = freq; } + eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL); msg = dpp_auth_resp_rx(auth, hdr, buf, len); if (!msg) { if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) { @@ -2162,6 +2233,7 @@ void wpas_dpp_deinit(struct wpa_supplicant *wpa_s) if (!wpa_s->dpp_init_done) return; eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL); offchannel_send_action_done(wpa_s); wpas_dpp_listen_stop(wpa_s); dpp_bootstrap_del(wpa_s, 0); diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index dc5c7b771..65454edf7 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1199,6 +1199,10 @@ struct wpa_supplicant { char *dpp_pkex_identifier; char *dpp_pkex_auth_cmd; char *dpp_configurator_params; + struct os_reltime dpp_last_init; + unsigned int dpp_init_max_tries; + unsigned int dpp_init_retry_time; + unsigned int dpp_resp_wait_time; #ifdef CONFIG_TESTING_OPTIONS char *dpp_config_obj_override; char *dpp_discovery_override;