From 16ef233bf1b0c9d3cc4d844622e7df6701edbe72 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 15 Sep 2019 16:19:45 +0300 Subject: [PATCH] DPP2: Connection status result (Enrollee) Add support for reporting connection status after provisioning if the Configurator requests this. Signed-off-by: Jouni Malinen --- src/common/dpp.c | 92 +++++++++++++++++ src/common/dpp.h | 5 + wpa_supplicant/dpp_supplicant.c | 165 +++++++++++++++++++++++++++++- wpa_supplicant/dpp_supplicant.h | 6 ++ wpa_supplicant/events.c | 22 ++++ wpa_supplicant/wpa_supplicant.c | 5 + wpa_supplicant/wpa_supplicant_i.h | 6 ++ 7 files changed, 298 insertions(+), 3 deletions(-) diff --git a/src/common/dpp.c b/src/common/dpp.c index ca84bea91..d35c82efb 100644 --- a/src/common/dpp.c +++ b/src/common/dpp.c @@ -6015,6 +6015,16 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth, if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0) goto fail; +#ifdef CONFIG_DPP2 + status = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_SEND_CONN_STATUS, &status_len); + if (status) { + wpa_printf(MSG_DEBUG, + "DPP: Configurator requested connection status result"); + auth->conn_status_requested = 1; + } +#endif /* CONFIG_DPP2 */ + ret = 0; fail: @@ -6291,6 +6301,88 @@ fail: return ret; } + +struct wpabuf * dpp_build_conn_status_result(struct dpp_authentication *auth, + enum dpp_status_error result, + const u8 *ssid, size_t ssid_len, + const char *channel_list) +{ + struct wpabuf *msg, *clear, *json; + size_t nonce_len, clear_len, attr_len; + const u8 *addr[2]; + size_t len[2]; + u8 *wrapped; + + json = wpabuf_alloc(1000); + if (!json) + return NULL; + wpabuf_printf(json, "{\"result\":%d", result); + if (ssid) { + char ssid_str[6 * SSID_MAX_LEN + 1]; + + wpabuf_put_str(json, ",\"ssid\":\""); + json_escape_string(ssid_str, sizeof(ssid_str), + (const char *) ssid, ssid_len); + wpabuf_put_str(json, ssid_str); + wpabuf_put_str(json, "\""); + } + if (channel_list) + wpabuf_printf(json, ",\"channelList\":\"%s\"", channel_list); + wpabuf_put_str(json, "}"); + wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON", + wpabuf_head(json), wpabuf_len(json)); + + nonce_len = auth->curve->nonce_len; + clear_len = 5 + 4 + nonce_len + 4 + wpabuf_len(json); + attr_len = 4 + clear_len + AES_BLOCK_SIZE; + clear = wpabuf_alloc(clear_len); + msg = dpp_alloc_msg(DPP_PA_CONNECTION_STATUS_RESULT, attr_len); + if (!clear || !msg) + goto fail; + + /* E-nonce */ + wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); + wpabuf_put_le16(clear, nonce_len); + wpabuf_put_data(clear, auth->e_nonce, nonce_len); + + /* DPP Connection Status */ + wpabuf_put_le16(clear, DPP_ATTR_CONN_STATUS); + wpabuf_put_le16(clear, wpabuf_len(json)); + wpabuf_put_buf(clear, json); + + /* OUI, OUI type, Crypto Suite, DPP frame type */ + addr[0] = wpabuf_head_u8(msg) + 2; + len[0] = 3 + 1 + 1 + 1; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + + /* Attributes before Wrapped Data (none) */ + addr[1] = wpabuf_put(msg, 0); + len[1] = 0; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + /* Wrapped Data */ + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + + wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); + if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, + wpabuf_head(clear), wpabuf_len(clear), + 2, addr, len, wrapped) < 0) + goto fail; + + wpa_hexdump_buf(MSG_DEBUG, "DPP: Connection Status Result attributes", + msg); + wpabuf_free(json); + wpabuf_free(clear); + return msg; +fail: + wpabuf_free(json); + wpabuf_free(clear); + wpabuf_free(msg); + return NULL; +} + #endif /* CONFIG_DPP2 */ diff --git a/src/common/dpp.h b/src/common/dpp.h index fa3fd76a0..d560f4b06 100644 --- a/src/common/dpp.h +++ b/src/common/dpp.h @@ -244,6 +244,7 @@ struct dpp_authentication { os_time_t net_access_key_expiry; struct wpabuf *c_sign_key; int send_conn_status; + int conn_status_requested; #ifdef CONFIG_TESTING_OPTIONS char *config_obj_override; char *discovery_override; @@ -451,6 +452,10 @@ enum dpp_status_error dpp_conn_status_result_rx(struct dpp_authentication *auth, size_t attr_len, u8 *ssid, size_t *ssid_len, char **channel_list); +struct wpabuf * dpp_build_conn_status_result(struct dpp_authentication *auth, + enum dpp_status_error result, + const u8 *ssid, size_t ssid_len, + const char *channel_list); struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type, size_t len); const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len); diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c index b9f46708c..020a6d022 100644 --- a/wpa_supplicant/dpp_supplicant.c +++ b/wpa_supplicant/dpp_supplicant.c @@ -148,6 +148,8 @@ static void wpas_dpp_auth_resp_retry(struct wpa_supplicant *wpa_s) static void wpas_dpp_try_to_connect(struct wpa_supplicant *wpa_s) { wpa_printf(MSG_DEBUG, "DPP: Trying to connect to the new network"); + wpa_s->suitable_network = 0; + wpa_s->no_suitable_network = 0; wpa_s->disconnected = 0; wpa_s->reassociate = 1; wpa_s->scan_runs = 0; @@ -157,6 +159,141 @@ static void wpas_dpp_try_to_connect(struct wpa_supplicant *wpa_s) } +#ifdef CONFIG_DPP2 + +static void wpas_dpp_conn_status_result_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct dpp_authentication *auth = wpa_s->dpp_auth; + enum dpp_status_error result; + + if (!auth || !auth->conn_status_requested) + return; + + wpa_printf(MSG_DEBUG, + "DPP: Connection timeout - report Connection Status Result"); + if (wpa_s->suitable_network) + result = DPP_STATUS_AUTH_FAILURE; + else if (wpa_s->no_suitable_network) + result = DPP_STATUS_NO_AP; + else + result = 255; /* What to report here for unexpected state? */ + wpas_dpp_send_conn_status_result(wpa_s, result); +} + + +static char * wpas_dpp_scan_channel_list(struct wpa_supplicant *wpa_s) +{ + char *str, *end, *pos; + size_t len; + unsigned int i; + u8 last_op_class = 0; + int res; + + if (!wpa_s->last_scan_freqs || !wpa_s->num_last_scan_freqs) + return NULL; + + len = wpa_s->num_last_scan_freqs * 8; + str = os_zalloc(len); + if (!str) + return NULL; + end = str + len; + pos = str; + + for (i = 0; i < wpa_s->num_last_scan_freqs; i++) { + enum hostapd_hw_mode mode; + u8 op_class, channel; + + mode = ieee80211_freq_to_channel_ext(wpa_s->last_scan_freqs[i], + 0, 0, &op_class, &channel); + if (mode == NUM_HOSTAPD_MODES) + continue; + if (op_class == last_op_class) + res = os_snprintf(pos, end - pos, ",%d", channel); + else + res = os_snprintf(pos, end - pos, "%s%d/%d", + pos == str ? "" : ",", + op_class, channel); + if (os_snprintf_error(end - pos, res)) { + *pos = '\0'; + break; + } + pos += res; + last_op_class = op_class; + } + + if (pos == str) { + os_free(str); + str = NULL; + } + return str; +} + + +void wpas_dpp_send_conn_status_result(struct wpa_supplicant *wpa_s, + enum dpp_status_error result) +{ + struct wpabuf *msg; + const char *channel_list = NULL; + char *channel_list_buf = NULL; + struct wpa_ssid *ssid = wpa_s->current_ssid; + struct dpp_authentication *auth = wpa_s->dpp_auth; + + eloop_cancel_timeout(wpas_dpp_conn_status_result_timeout, wpa_s, NULL); + + if (!auth || !auth->conn_status_requested) + return; + auth->conn_status_requested = 0; + wpa_printf(MSG_DEBUG, "DPP: Report connection status result %d", + result); + + if (result == DPP_STATUS_NO_AP) { + channel_list_buf = wpas_dpp_scan_channel_list(wpa_s); + channel_list = channel_list_buf; + } + + msg = dpp_build_conn_status_result(auth, result, + ssid ? ssid->ssid : + wpa_s->dpp_last_ssid, + ssid ? ssid->ssid_len : + wpa_s->dpp_last_ssid_len, + channel_list); + os_free(channel_list_buf); + if (!msg) { + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; + return; + } + + wpa_msg(wpa_s, MSG_INFO, + DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(auth->peer_mac_addr), auth->curr_freq, + DPP_PA_CONNECTION_STATUS_RESULT); + offchannel_send_action(wpa_s, auth->curr_freq, + auth->peer_mac_addr, wpa_s->own_addr, broadcast, + wpabuf_head(msg), wpabuf_len(msg), + 500, wpas_dpp_tx_status, 0); + wpabuf_free(msg); + + /* This exchange will be terminated in the TX status handler */ + auth->remove_on_tx_status = 1; + + return; +} + + +void wpas_dpp_connected(struct wpa_supplicant *wpa_s) +{ + struct dpp_authentication *auth = wpa_s->dpp_auth; + + if (auth && auth->conn_status_requested) + wpas_dpp_send_conn_status_result(wpa_s, DPP_STATUS_OK); +} + +#endif /* CONFIG_DPP2 */ + + static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, @@ -182,18 +319,30 @@ static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s, #ifdef CONFIG_DPP2 if (auth->connect_on_tx_status) { + auth->connect_on_tx_status = 0; wpa_printf(MSG_DEBUG, "DPP: Try to connect after completed configuration result"); wpas_dpp_try_to_connect(wpa_s); - dpp_auth_deinit(wpa_s->dpp_auth); - wpa_s->dpp_auth = NULL; + if (auth->conn_status_requested) { + wpa_printf(MSG_DEBUG, + "DPP: Start 15 second timeout for reporting connection status result"); + eloop_cancel_timeout( + wpas_dpp_conn_status_result_timeout, + wpa_s, NULL); + eloop_register_timeout( + 15, 0, wpas_dpp_conn_status_result_timeout, + wpa_s, NULL); + } else { + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; + } return; } #endif /* CONFIG_DPP2 */ if (wpa_s->dpp_auth->remove_on_tx_status) { wpa_printf(MSG_DEBUG, - "DPP: Terminate authentication exchange due to an earlier error"); + "DPP: Terminate authentication exchange due to a request to do so on TX status"); eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, @@ -899,6 +1048,9 @@ static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s, } } + os_memcpy(wpa_s->dpp_last_ssid, auth->ssid, auth->ssid_len); + wpa_s->dpp_last_ssid_len = auth->ssid_len; + return ssid; fail: wpas_notify_network_removed(wpa_s, ssid); @@ -1452,6 +1604,9 @@ static void wpas_dpp_rx_peer_disc_resp(struct wpa_supplicant *wpa_s, status[0]); wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR " status=%u", MAC2STR(src), status[0]); +#ifdef CONFIG_DPP2 + wpas_dpp_send_conn_status_result(wpa_s, status[0]); +#endif /* CONFIG_DPP2 */ goto fail; } @@ -1475,6 +1630,9 @@ static void wpas_dpp_rx_peer_disc_resp(struct wpa_supplicant *wpa_s, "DPP: Network Introduction protocol resulted in failure"); wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR " fail=peer_connector_validation_failed", MAC2STR(src)); +#ifdef CONFIG_DPP2 + wpas_dpp_send_conn_status_result(wpa_s, res); +#endif /* CONFIG_DPP2 */ goto fail; } @@ -2368,6 +2526,7 @@ void wpas_dpp_deinit(struct wpa_supplicant *wpa_s) eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_dpp_conn_status_result_wait_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_dpp_conn_status_result_timeout, wpa_s, NULL); dpp_pfs_free(wpa_s->dpp_pfs); wpa_s->dpp_pfs = NULL; #endif /* CONFIG_DPP2 */ diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h index 9ba315f55..b33798265 100644 --- a/wpa_supplicant/dpp_supplicant.h +++ b/wpa_supplicant/dpp_supplicant.h @@ -1,6 +1,7 @@ /* * wpa_supplicant - DPP * Copyright (c) 2017, Qualcomm Atheros, Inc. + * Copyright (c) 2018-2019, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,6 +10,8 @@ #ifndef DPP_SUPPLICANT_H #define DPP_SUPPLICANT_H +enum dpp_status_error; + int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd); int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd); int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd); @@ -26,5 +29,8 @@ void wpas_dpp_deinit(struct wpa_supplicant *wpa_s); int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss); int wpas_dpp_controller_start(struct wpa_supplicant *wpa_s, const char *cmd); +void wpas_dpp_connected(struct wpa_supplicant *wpa_s); +void wpas_dpp_send_conn_status_result(struct wpa_supplicant *wpa_s, + enum dpp_status_error result); #endif /* DPP_SUPPLICANT_H */ diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index f28e4399e..2e083fe4e 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1941,6 +1941,21 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, radio_work_done(work); } + os_free(wpa_s->last_scan_freqs); + wpa_s->last_scan_freqs = NULL; + wpa_s->num_last_scan_freqs = 0; + if (own_request && data && + data->scan_info.freqs && data->scan_info.num_freqs) { + wpa_s->last_scan_freqs = os_malloc(sizeof(int) * + data->scan_info.num_freqs); + if (wpa_s->last_scan_freqs) { + os_memcpy(wpa_s->last_scan_freqs, + data->scan_info.freqs, + sizeof(int) * data->scan_info.num_freqs); + wpa_s->num_last_scan_freqs = data->scan_info.num_freqs; + } + } + return wpas_select_network_from_last_scan(wpa_s, 1, own_request); scan_work_done: @@ -1994,6 +2009,8 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, return 0; } + wpa_s->suitable_network++; + if (ssid != wpa_s->current_ssid && wpa_s->wpa_state >= WPA_AUTHENTICATING) { wpa_s->own_disconnect_req = 1; @@ -2014,6 +2031,7 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, */ return 1; } else { + wpa_s->no_suitable_network++; wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found"); ssid = wpa_supplicant_pick_new_network(wpa_s); if (ssid) { @@ -3066,6 +3084,10 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, if (wpas_p2p_4way_hs_failed(wpa_s) > 0) return; /* P2P group removed */ wpas_auth_failed(wpa_s, "WRONG_KEY"); +#ifdef CONFIG_DPP2 + wpas_dpp_send_conn_status_result(wpa_s, + DPP_STATUS_AUTH_FAILURE); +#endif /* CONFIG_DPP2 */ } if (!wpa_s->disconnected && (!wpa_s->auto_reconnect_disabled || diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 709a33fa8..6fb4efb5f 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -985,6 +985,10 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, if (wpa_s->wpa_state == WPA_COMPLETED || old_state == WPA_COMPLETED) wpas_notify_auth_changed(wpa_s); +#ifdef CONFIG_DPP2 + if (wpa_s->wpa_state == WPA_COMPLETED) + wpas_dpp_connected(wpa_s); +#endif /* CONFIG_DPP2 */ } #if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) if (update_fils_connect_params) @@ -6079,6 +6083,7 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, } os_free(wpa_s->ssids_from_scan_req); + os_free(wpa_s->last_scan_freqs); os_free(wpa_s); } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 32f390fd9..872c19c84 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -700,6 +700,10 @@ struct wpa_supplicant { struct wpa_ssid_value *ssids_from_scan_req; unsigned int num_ssids_from_scan_req; + int *last_scan_freqs; + unsigned int num_last_scan_freqs; + unsigned int suitable_network; + unsigned int no_suitable_network; u64 drv_flags; unsigned int drv_enc; @@ -1237,6 +1241,8 @@ struct wpa_supplicant { unsigned int dpp_resp_wait_time; unsigned int dpp_resp_max_tries; unsigned int dpp_resp_retry_time; + u8 dpp_last_ssid[SSID_MAX_LEN]; + size_t dpp_last_ssid_len; #ifdef CONFIG_DPP2 struct dpp_pfs *dpp_pfs; #endif /* CONFIG_DPP2 */ -- 2.39.2