From b10e01a795323fbe680475483c5c75b1fc3ceb68 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 (Configurator) A new argument to the DPP_AUTH_INIT command (conn_status=1) can now be used to set Configurator to request a station Enrollee to report connection result after a successfully completed provisioning step. If the peer supports this, the DPP-CONF-SENT event indicates this with a new argument (wait_conn_status=1) and the Configurator remains waiting for the connection result for up to 16 seconds. Once the Enrollee reports the result, a new DPP-CONN-STATUS-RESULT event is generated with arguments result, ssid, and channel_list indicating what the Enrollee reported. result=0 means success while non-zero codes are for various error cases as specified in the DPP tech spec. If no report is received from the Enrollee, the event with "timeout" argument is generated locally. Signed-off-by: Jouni Malinen --- src/ap/dpp_hostapd.c | 72 +++++++++++ src/common/dpp.c | 215 +++++++++++++++++++++++++++++++- src/common/dpp.h | 8 ++ src/common/wpa_ctrl.h | 1 + wpa_supplicant/dpp_supplicant.c | 74 +++++++++++ 5 files changed, 369 insertions(+), 1 deletion(-) diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c index 697c3bad3..6c3e11e2d 100644 --- a/src/ap/dpp_hostapd.c +++ b/src/ap/dpp_hostapd.c @@ -922,6 +922,24 @@ static void hostapd_dpp_config_result_wait_timeout(void *eloop_ctx, } +static void hostapd_dpp_conn_status_result_wait_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct dpp_authentication *auth = hapd->dpp_auth; + + if (!auth || !auth->waiting_conf_result) + return; + + wpa_printf(MSG_DEBUG, + "DPP: Timeout while waiting for Connection Status Result"); + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_CONN_STATUS_RESULT "timeout"); + dpp_auth_deinit(auth); + hapd->dpp_auth = NULL; +} + + static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src, const u8 *hdr, const u8 *buf, size_t len) { @@ -945,6 +963,20 @@ static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src, status = dpp_conf_result_rx(auth, hdr, buf, len); + if (status == DPP_STATUS_OK && auth->send_conn_status) { + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_CONF_SENT "wait_conn_status=1"); + wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result"); + eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, + hapd, NULL); + eloop_cancel_timeout( + hostapd_dpp_conn_status_result_wait_timeout, + hapd, NULL); + eloop_register_timeout( + 16, 0, hostapd_dpp_conn_status_result_wait_timeout, + hapd, NULL); + return; + } hostapd_drv_send_action_cancel_wait(hapd); hostapd_dpp_listen_stop(hapd); if (status == DPP_STATUS_OK) @@ -957,6 +989,41 @@ static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src, NULL); } + +static void hostapd_dpp_rx_conn_status_result(struct hostapd_data *hapd, + const u8 *src, const u8 *hdr, + const u8 *buf, size_t len) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + enum dpp_status_error status; + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len = 0; + char *channel_list = NULL; + + wpa_printf(MSG_DEBUG, "DPP: Connection Status Result"); + + if (!auth || !auth->waiting_conn_status_result) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Configuration waiting for connection status result - drop"); + return; + } + + status = dpp_conn_status_result_rx(auth, hdr, buf, len, + ssid, &ssid_len, &channel_list); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONN_STATUS_RESULT + "result=%d ssid=%s channel_list=%s", + status, wpa_ssid_txt(ssid, ssid_len), + channel_list ? channel_list : "N/A"); + os_free(channel_list); + hostapd_drv_send_action_cancel_wait(hapd); + hostapd_dpp_listen_stop(hapd); + dpp_auth_deinit(auth); + hapd->dpp_auth = NULL; + eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout, + hapd, NULL); +} + + #endif /* CONFIG_DPP2 */ @@ -1403,6 +1470,9 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, case DPP_PA_CONFIGURATION_RESULT: hostapd_dpp_rx_conf_result(hapd, src, hdr, buf, len); break; + case DPP_PA_CONNECTION_STATUS_RESULT: + hostapd_dpp_rx_conn_status_result(hapd, src, hdr, buf, len); + break; #endif /* CONFIG_DPP2 */ default: wpa_printf(MSG_DEBUG, @@ -1715,6 +1785,8 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd) #ifdef CONFIG_DPP2 eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout, hapd, + NULL); #endif /* CONFIG_DPP2 */ dpp_auth_deinit(hapd->dpp_auth); hapd->dpp_auth = NULL; diff --git a/src/common/dpp.c b/src/common/dpp.c index 5500cb24c..ca84bea91 100644 --- a/src/common/dpp.c +++ b/src/common/dpp.c @@ -4446,6 +4446,12 @@ int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx, } } + pos = os_strstr(cmd, " conn_status="); + if (pos) { + pos += 13; + auth->send_conn_status = atoi(pos); + } + if (dpp_configuration_parse(auth, cmd) < 0) { wpa_msg(msg_ctx, MSG_INFO, "DPP: Failed to set configurator parameters"); @@ -4861,10 +4867,12 @@ dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE; auth->conf_resp_status = status; - /* { E-nonce, configurationObject}ke */ + /* { E-nonce, configurationObject[, sendConnStatus]}ke */ clear_len = 4 + e_nonce_len; if (conf) clear_len += 4 + wpabuf_len(conf); + if (auth->peer_version >= 2 && auth->send_conn_status && !ap) + clear_len += 4; clear = wpabuf_alloc(clear_len); attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE; #ifdef CONFIG_TESTING_OPTIONS @@ -4913,6 +4921,12 @@ skip_e_nonce: wpabuf_put_buf(clear, conf); } + if (auth->peer_version >= 2 && auth->send_conn_status && !ap) { + wpa_printf(MSG_DEBUG, "DPP: sendConnStatus"); + wpabuf_put_le16(clear, DPP_ATTR_SEND_CONN_STATUS); + wpabuf_put_le16(clear, 0); + } + #ifdef CONFIG_TESTING_OPTIONS skip_config_obj: if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) { @@ -6148,6 +6162,135 @@ fail: return NULL; } + +static int valid_channel_list(const char *val) +{ + while (*val) { + if (!((*val >= '0' && *val <= '9') || + *val == '/' || *val == ',')) + return 0; + val++; + } + + return 1; +} + + +enum dpp_status_error dpp_conn_status_result_rx(struct dpp_authentication *auth, + const u8 *hdr, + const u8 *attr_start, + size_t attr_len, + u8 *ssid, size_t *ssid_len, + char **channel_list) +{ + const u8 *wrapped_data, *status, *e_nonce; + u16 wrapped_data_len, status_len, e_nonce_len; + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + enum dpp_status_error ret = 256; + struct json_token *root = NULL, *token; + + *ssid_len = 0; + *channel_list = NULL; + + 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) { + dpp_auth_fail(auth, + "Missing or invalid required Wrapped Data attribute"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", + wrapped_data, wrapped_data_len); + + attr_len = wrapped_data - 4 - attr_start; + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + e_nonce = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_ENROLLEE_NONCE, + &e_nonce_len); + if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, + "Missing or invalid Enrollee Nonce attribute"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); + if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) { + dpp_auth_fail(auth, "Enrollee Nonce mismatch"); + wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce", + auth->e_nonce, e_nonce_len); + goto fail; + } + + status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_CONN_STATUS, + &status_len); + if (!status) { + dpp_auth_fail(auth, + "Missing required DPP Connection Status attribute"); + goto fail; + } + wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON", + status, status_len); + + root = json_parse((const char *) status, status_len); + if (!root) { + dpp_auth_fail(auth, "Could not parse connStatus"); + goto fail; + } + + token = json_get_member(root, "ssid"); + if (token && token->type == JSON_STRING && + os_strlen(token->string) <= SSID_MAX_LEN) { + *ssid_len = os_strlen(token->string); + os_memcpy(ssid, token->string, *ssid_len); + } + + token = json_get_member(root, "channelList"); + if (token && token->type == JSON_STRING && + valid_channel_list(token->string)) + *channel_list = os_strdup(token->string); + + token = json_get_member(root, "result"); + if (!token || token->type != JSON_NUMBER) { + dpp_auth_fail(auth, "No connStatus - result"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: result %d", token->number); + ret = token->number; + +fail: + json_free(root); + bin_clear_free(unwrapped, unwrapped_len); + return ret; +} + #endif /* CONFIG_DPP2 */ @@ -8721,6 +8864,10 @@ int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id, #ifdef CONFIG_DPP2 +static void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx, + void *timeout_ctx); + + static void dpp_connection_free(struct dpp_connection *conn) { if (conn->sock >= 0) { @@ -8730,6 +8877,8 @@ static void dpp_connection_free(struct dpp_connection *conn) eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE); close(conn->sock); } + eloop_cancel_timeout(dpp_controller_conn_status_result_wait_timeout, + conn, NULL); wpabuf_free(conn->msg); wpabuf_free(conn->msg_out); dpp_auth_deinit(conn->auth); @@ -9454,6 +9603,22 @@ static int dpp_controller_rx_auth_conf(struct dpp_connection *conn, } +static void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct dpp_connection *conn = eloop_ctx; + + if (!conn->auth->waiting_conf_result) + return; + + wpa_printf(MSG_DEBUG, + "DPP: Timeout while waiting for Connection Status Result"); + wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO, + DPP_EVENT_CONN_STATUS_RESULT "timeout"); + dpp_connection_remove(conn); +} + + static int dpp_controller_rx_conf_result(struct dpp_connection *conn, const u8 *hdr, const u8 *buf, size_t len) @@ -9473,6 +9638,18 @@ static int dpp_controller_rx_conf_result(struct dpp_connection *conn, } status = dpp_conf_result_rx(auth, hdr, buf, len); + if (status == DPP_STATUS_OK && auth->send_conn_status) { + wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO, + DPP_EVENT_CONF_SENT "wait_conn_status=1"); + wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result"); + eloop_cancel_timeout( + dpp_controller_conn_status_result_wait_timeout, + conn, NULL); + eloop_register_timeout( + 16, 0, dpp_controller_conn_status_result_wait_timeout, + conn, NULL); + return 0; + } if (status == DPP_STATUS_OK) wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT); @@ -9483,6 +9660,39 @@ static int dpp_controller_rx_conf_result(struct dpp_connection *conn, } +static int dpp_controller_rx_conn_status_result(struct dpp_connection *conn, + const u8 *hdr, const u8 *buf, + size_t len) +{ + struct dpp_authentication *auth = conn->auth; + enum dpp_status_error status; + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len = 0; + char *channel_list = NULL; + + if (!conn->ctrl) + return 0; + + wpa_printf(MSG_DEBUG, "DPP: Connection Status Result"); + + if (!auth || !auth->waiting_conn_status_result) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Configuration waiting for connection status result - drop"); + return -1; + } + + status = dpp_conn_status_result_rx(auth, hdr, buf, len, + ssid, &ssid_len, &channel_list); + wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO, + DPP_EVENT_CONN_STATUS_RESULT + "result=%d ssid=%s channel_list=%s", + status, wpa_ssid_txt(ssid, ssid_len), + channel_list ? channel_list : "N/A"); + os_free(channel_list); + return -1; /* to remove the completed connection */ +} + + static int dpp_controller_rx_action(struct dpp_connection *conn, const u8 *msg, size_t len) { @@ -9530,6 +9740,9 @@ static int dpp_controller_rx_action(struct dpp_connection *conn, const u8 *msg, return dpp_controller_rx_auth_conf(conn, msg, pos, end - pos); case DPP_PA_CONFIGURATION_RESULT: return dpp_controller_rx_conf_result(conn, msg, pos, end - pos); + case DPP_PA_CONNECTION_STATUS_RESULT: + return dpp_controller_rx_conn_status_result(conn, msg, pos, + end - pos); default: /* TODO: missing messages types */ wpa_printf(MSG_DEBUG, diff --git a/src/common/dpp.h b/src/common/dpp.h index 7e8d9554f..fa3fd76a0 100644 --- a/src/common/dpp.h +++ b/src/common/dpp.h @@ -226,6 +226,7 @@ struct dpp_authentication { int remove_on_tx_status; int connect_on_tx_status; int waiting_conf_result; + int waiting_conn_status_result; int auth_success; struct wpabuf *conf_req; const struct wpabuf *conf_resp; /* owned by GAS server */ @@ -242,6 +243,7 @@ struct dpp_authentication { struct wpabuf *net_access_key; os_time_t net_access_key_expiry; struct wpabuf *c_sign_key; + int send_conn_status; #ifdef CONFIG_TESTING_OPTIONS char *config_obj_override; char *discovery_override; @@ -443,6 +445,12 @@ enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth, const u8 *attr_start, size_t attr_len); struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth, enum dpp_status_error status); +enum dpp_status_error dpp_conn_status_result_rx(struct dpp_authentication *auth, + const u8 *hdr, + const u8 *attr_start, + size_t attr_len, + u8 *ssid, size_t *ssid_len, + 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/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index b24ae63e5..70ecf5d82 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -168,6 +168,7 @@ extern "C" { #define DPP_EVENT_CONF_RECEIVED "DPP-CONF-RECEIVED " #define DPP_EVENT_CONF_SENT "DPP-CONF-SENT " #define DPP_EVENT_CONF_FAILED "DPP-CONF-FAILED " +#define DPP_EVENT_CONN_STATUS_RESULT "DPP-CONN-STATUS-RESULT " #define DPP_EVENT_CONFOBJ_AKM "DPP-CONFOBJ-AKM " #define DPP_EVENT_CONFOBJ_SSID "DPP-CONFOBJ-SSID " #define DPP_EVENT_CONFOBJ_PASS "DPP-CONFOBJ-PASS " diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c index 1f65658ef..b9f46708c 100644 --- a/wpa_supplicant/dpp_supplicant.c +++ b/wpa_supplicant/dpp_supplicant.c @@ -1270,6 +1270,24 @@ static void wpas_dpp_config_result_wait_timeout(void *eloop_ctx, } +static void wpas_dpp_conn_status_result_wait_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct dpp_authentication *auth = wpa_s->dpp_auth; + + if (!auth || !auth->waiting_conn_status_result) + return; + + wpa_printf(MSG_DEBUG, + "DPP: Timeout while waiting for Connection Status Result"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONN_STATUS_RESULT "timeout"); + wpas_dpp_listen_stop(wpa_s); + dpp_auth_deinit(auth); + wpa_s->dpp_auth = NULL; +} + + static void wpas_dpp_rx_conf_result(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *hdr, const u8 *buf, size_t len) { @@ -1293,6 +1311,23 @@ static void wpas_dpp_rx_conf_result(struct wpa_supplicant *wpa_s, const u8 *src, status = dpp_conf_result_rx(auth, hdr, buf, len); + if (status == DPP_STATUS_OK && auth->send_conn_status) { + wpa_msg(wpa_s, MSG_INFO, + DPP_EVENT_CONF_SENT "wait_conn_status=1"); + wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result"); + eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, + wpa_s, NULL); + auth->waiting_conn_status_result = 1; + eloop_cancel_timeout(wpas_dpp_conn_status_result_wait_timeout, + wpa_s, NULL); + eloop_register_timeout(16, 0, + wpas_dpp_conn_status_result_wait_timeout, + wpa_s, NULL); + offchannel_send_action_done(wpa_s); + wpas_dpp_listen_start(wpa_s, auth->neg_freq ? auth->neg_freq : + auth->curr_freq); + return; + } offchannel_send_action_done(wpa_s); wpas_dpp_listen_stop(wpa_s); if (status == DPP_STATUS_OK) @@ -1305,6 +1340,40 @@ static void wpas_dpp_rx_conf_result(struct wpa_supplicant *wpa_s, const u8 *src, } +static void wpas_dpp_rx_conn_status_result(struct wpa_supplicant *wpa_s, + const u8 *src, const u8 *hdr, + const u8 *buf, size_t len) +{ + struct dpp_authentication *auth = wpa_s->dpp_auth; + enum dpp_status_error status; + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len = 0; + char *channel_list = NULL; + + wpa_printf(MSG_DEBUG, "DPP: Connection Status Result"); + + if (!auth || !auth->waiting_conn_status_result) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Configuration waiting for connection status result - drop"); + return; + } + + status = dpp_conn_status_result_rx(auth, hdr, buf, len, + ssid, &ssid_len, &channel_list); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONN_STATUS_RESULT + "result=%d ssid=%s channel_list=%s", + status, wpa_ssid_txt(ssid, ssid_len), + channel_list ? channel_list : "N/A"); + os_free(channel_list); + offchannel_send_action_done(wpa_s); + wpas_dpp_listen_stop(wpa_s); + dpp_auth_deinit(auth); + wpa_s->dpp_auth = NULL; + eloop_cancel_timeout(wpas_dpp_conn_status_result_wait_timeout, + wpa_s, NULL); +} + + static int wpas_dpp_process_conf_obj(void *ctx, struct dpp_authentication *auth) { @@ -1848,6 +1917,9 @@ void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, case DPP_PA_CONFIGURATION_RESULT: wpas_dpp_rx_conf_result(wpa_s, src, hdr, buf, len); break; + case DPP_PA_CONNECTION_STATUS_RESULT: + wpas_dpp_rx_conn_status_result(wpa_s, src, hdr, buf, len); + break; #endif /* CONFIG_DPP2 */ default: wpa_printf(MSG_DEBUG, @@ -2294,6 +2366,8 @@ void wpas_dpp_deinit(struct wpa_supplicant *wpa_s) eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL); #ifdef CONFIG_DPP2 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); dpp_pfs_free(wpa_s->dpp_pfs); wpa_s->dpp_pfs = NULL; #endif /* CONFIG_DPP2 */