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