DPP2: Connection status result (Enrollee)

Add support for reporting connection status after provisioning if the
Configurator requests this.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
Jouni Malinen 2019-09-15 16:19:45 +03:00 committed by Jouni Malinen
parent b10e01a795
commit 16ef233bf1
7 changed files with 298 additions and 3 deletions

View File

@ -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 */

View File

@ -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);

View File

@ -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 */

View File

@ -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 */

View File

@ -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 ||

View File

@ -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);
}

View File

@ -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 */