]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
DPP2: Configuration Result message generation and processing
authorJouni Malinen <jouni@codeaurora.org>
Thu, 14 Mar 2019 15:05:02 +0000 (17:05 +0200)
committerJouni Malinen <j@w1.fi>
Thu, 14 Mar 2019 22:31:09 +0000 (00:31 +0200)
Use this new message from Enrollee to Configurator to indicate result of
the config object provisioning if both devices support protocol version
2 or newer.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
src/ap/dpp_hostapd.c
src/common/dpp.c
src/common/dpp.h
wpa_supplicant/dpp_supplicant.c

index 149f389f789f1f4adbb3b0e9da1da824e2741da8..a84654d2d0ddca9b190c823e6c4ba196934b60b6 100644 (file)
@@ -354,6 +354,16 @@ void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
                return;
        }
 
+#ifdef CONFIG_DPP2
+       if (auth->connect_on_tx_status) {
+               wpa_printf(MSG_DEBUG,
+                          "DPP: Complete exchange on configuration result");
+               dpp_auth_deinit(hapd->dpp_auth);
+               hapd->dpp_auth = NULL;
+               return;
+       }
+#endif /* CONFIG_DPP2 */
+
        if (hapd->dpp_auth->remove_on_tx_status) {
                wpa_printf(MSG_DEBUG,
                           "DPP: Terminate authentication exchange due to an earlier error");
@@ -1072,6 +1082,7 @@ static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
        struct hostapd_data *hapd = ctx;
        const u8 *pos;
        struct dpp_authentication *auth = hapd->dpp_auth;
+       enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED;
 
        if (!auth || !auth->auth_success) {
                wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
@@ -1107,12 +1118,35 @@ static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
        }
 
        hostapd_dpp_handle_config_obj(hapd, auth);
-       dpp_auth_deinit(hapd->dpp_auth);
-       hapd->dpp_auth = NULL;
-       return;
-
+       status = DPP_STATUS_OK;
 fail:
-       wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+       if (status != DPP_STATUS_OK)
+               wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+#ifdef CONFIG_DPP2
+       if (auth->peer_version >= 2 &&
+           auth->conf_resp_status == DPP_STATUS_OK) {
+               struct wpabuf *msg;
+
+               wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
+               msg = dpp_build_conf_result(auth, status);
+               if (!msg)
+                       goto fail2;
+
+               wpa_msg(hapd->msg_ctx, MSG_INFO,
+                       DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+                       MAC2STR(addr), auth->curr_freq,
+                       DPP_PA_CONFIGURATION_RESULT);
+               hostapd_drv_send_action(hapd, auth->curr_freq, 0,
+                                       addr, wpabuf_head(msg),
+                                       wpabuf_len(msg));
+               wpabuf_free(msg);
+
+               /* This exchange will be terminated in the TX status handler */
+               auth->connect_on_tx_status = 1;
+               return;
+       }
+fail2:
+#endif /* CONFIG_DPP2 */
        dpp_auth_deinit(hapd->dpp_auth);
        hapd->dpp_auth = NULL;
 }
@@ -1281,6 +1315,63 @@ static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src,
 }
 
 
+#ifdef CONFIG_DPP2
+
+static void hostapd_dpp_config_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 Configuration Result");
+       wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+       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)
+{
+       struct dpp_authentication *auth = hapd->dpp_auth;
+       enum dpp_status_error status;
+
+       wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR,
+                  MAC2STR(src));
+
+       if (!auth || !auth->waiting_conf_result) {
+               wpa_printf(MSG_DEBUG,
+                          "DPP: No DPP Configuration waiting for result - drop");
+               return;
+       }
+
+       if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+                          MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+               return;
+       }
+
+       status = dpp_conf_result_rx(auth, hdr, buf, len);
+
+       hostapd_drv_send_action_cancel_wait(hapd);
+       hostapd_dpp_listen_stop(hapd);
+       if (status == DPP_STATUS_OK)
+               wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+       else
+               wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+       dpp_auth_deinit(auth);
+       hapd->dpp_auth = NULL;
+       eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
+                            NULL);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
 static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd,
                                            const u8 *src, unsigned int freq,
                                            u8 trans_id,
@@ -1744,6 +1835,11 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
                hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len,
                                                       freq);
                break;
+#ifdef CONFIG_DPP2
+       case DPP_PA_CONFIGURATION_RESULT:
+               hostapd_dpp_rx_conf_result(hapd, src, hdr, buf, len);
+               break;
+#endif /* CONFIG_DPP2 */
        default:
                wpa_printf(MSG_DEBUG,
                           "DPP: Ignored unsupported frame subtype %d", type);
@@ -1790,11 +1886,28 @@ hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
 
 void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok)
 {
-       if (!hapd->dpp_auth)
+       struct dpp_authentication *auth = hapd->dpp_auth;
+
+       if (!auth)
                return;
 
+       wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)",
+                  ok);
        eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
        eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+#ifdef CONFIG_DPP2
+       if (ok && auth->peer_version >= 2 &&
+           auth->conf_resp_status == DPP_STATUS_OK) {
+               wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
+               auth->waiting_conf_result = 1;
+               eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout,
+                                    hapd, NULL);
+               eloop_register_timeout(2, 0,
+                                      hostapd_dpp_config_result_wait_timeout,
+                                      hapd, NULL);
+               return;
+       }
+#endif /* CONFIG_DPP2 */
        hostapd_drv_send_action_cancel_wait(hapd);
 
        if (ok)
@@ -2070,6 +2183,10 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd)
        eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
        eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
        eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+#ifdef CONFIG_DPP2
+       eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
+                            NULL);
+#endif /* CONFIG_DPP2 */
        dpp_auth_deinit(hapd->dpp_auth);
        hapd->dpp_auth = NULL;
        hostapd_dpp_pkex_remove(hapd, "*");
index 00208128b22dd8c2b2843234dbebe161cc1fa45f..c7a200780395f8b5a096488b6eb1693497b33b77 100644 (file)
@@ -4441,6 +4441,7 @@ dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
                                  wpabuf_head(conf), wpabuf_len(conf));
        }
        status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE;
+       auth->conf_resp_status = status;
 
        /* { E-nonce, configurationObject}ke */
        clear_len = 4 + e_nonce_len;
@@ -4613,6 +4614,7 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
                goto fail;
        }
        wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
+       os_memcpy(auth->e_nonce, e_nonce, e_nonce_len);
 
        config_attr = dpp_get_attr(unwrapped, unwrapped_len,
                                   DPP_ATTR_CONFIG_ATTR_OBJ,
@@ -5486,6 +5488,8 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth,
        size_t unwrapped_len = 0;
        int ret = -1;
 
+       auth->conf_resp_status = 255;
+
        if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) {
                dpp_auth_fail(auth, "Invalid attribute in config response");
                return -1;
@@ -5546,6 +5550,7 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth,
                              "Missing or invalid required DPP Status attribute");
                goto fail;
        }
+       auth->conf_resp_status = status[0];
        wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
        if (status[0] != DPP_STATUS_OK) {
                dpp_auth_fail(auth, "Configurator rejected configuration");
@@ -5572,6 +5577,146 @@ fail:
 }
 
 
+#ifdef CONFIG_DPP2
+enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth,
+                                        const u8 *hdr,
+                                        const u8 *attr_start, size_t attr_len)
+{
+       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;
+
+       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_STATUS,
+                             &status_len);
+       if (!status || status_len < 1) {
+               dpp_auth_fail(auth,
+                             "Missing or invalid required DPP Status attribute");
+               goto fail;
+       }
+       wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+       ret = status[0];
+
+fail:
+       bin_clear_free(unwrapped, unwrapped_len);
+       return ret;
+}
+#endif /* CONFIG_DPP2 */
+
+
+struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth,
+                                     enum dpp_status_error status)
+{
+       struct wpabuf *msg, *clear;
+       size_t nonce_len, clear_len, attr_len;
+       const u8 *addr[2];
+       size_t len[2];
+       u8 *wrapped;
+
+       nonce_len = auth->curve->nonce_len;
+       clear_len = 5 + 4 + nonce_len;
+       attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+       clear = wpabuf_alloc(clear_len);
+       msg = dpp_alloc_msg(DPP_PA_CONFIGURATION_RESULT, attr_len);
+       if (!clear || !msg)
+               return NULL;
+
+       /* DPP Status */
+       dpp_build_attr_status(clear, status);
+
+       /* 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);
+
+       /* 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: Configuration Result attributes", msg);
+       wpabuf_free(clear);
+       return msg;
+fail:
+       wpabuf_free(clear);
+       wpabuf_free(msg);
+       return NULL;
+}
+
+
 void dpp_configurator_free(struct dpp_configurator *conf)
 {
        if (!conf)
index 4b192ac0b6d7ab358b4c8a7af50e555a3a2d96c9..4af913bc1f4c362b5c74abeb37b7e1f576e9191a 100644 (file)
@@ -28,6 +28,7 @@ enum dpp_public_action_frame_type {
        DPP_PA_PKEX_EXCHANGE_RESP = 8,
        DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9,
        DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10,
+       DPP_PA_CONFIGURATION_RESULT = 11,
 };
 
 enum dpp_attribute_id {
@@ -69,6 +70,7 @@ enum dpp_status_error {
        DPP_STATUS_RESPONSE_PENDING = 6,
        DPP_STATUS_INVALID_CONNECTOR = 7,
        DPP_STATUS_NO_MATCH = 8,
+       DPP_STATUS_CONFIG_REJECTED = 9,
 };
 
 #define DPP_CAPAB_ENROLLEE BIT(0)
@@ -173,6 +175,7 @@ struct dpp_authentication {
        u8 waiting_pubkey_hash[SHA256_MAC_LEN];
        int response_pending;
        enum dpp_status_error auth_resp_status;
+       enum dpp_status_error conf_resp_status;
        u8 peer_mac_addr[ETH_ALEN];
        u8 i_nonce[DPP_MAX_NONCE_LEN];
        u8 r_nonce[DPP_MAX_NONCE_LEN];
@@ -208,6 +211,8 @@ struct dpp_authentication {
        u8 allowed_roles;
        int configurator;
        int remove_on_tx_status;
+       int connect_on_tx_status;
+       int waiting_conf_result;
        int auth_success;
        struct wpabuf *conf_req;
        const struct wpabuf *conf_resp; /* owned by GAS server */
@@ -393,6 +398,11 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
                size_t attr_len);
 int dpp_conf_resp_rx(struct dpp_authentication *auth,
                     const struct wpabuf *resp);
+enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth,
+                                        const u8 *hdr,
+                                        const u8 *attr_start, size_t attr_len);
+struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth,
+                                     enum dpp_status_error status);
 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);
index 74f71701f3917bd1f553c7dddb8dfafccdc11b34..8de53ad8ba55c991ac7cf84d18033a816974cd01 100644 (file)
@@ -364,6 +364,18 @@ 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->disconnected = 0;
+       wpa_s->reassociate = 1;
+       wpa_s->scan_runs = 0;
+       wpa_s->normal_scans = 0;
+       wpa_supplicant_cancel_sched_scan(wpa_s);
+       wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
 static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
                               unsigned int freq, const u8 *dst,
                               const u8 *src, const u8 *bssid,
@@ -387,6 +399,17 @@ static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
                return;
        }
 
+#ifdef CONFIG_DPP2
+       if (auth->connect_on_tx_status) {
+               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;
+               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");
@@ -1165,6 +1188,27 @@ static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s,
 {
        struct wpa_ssid *ssid;
 
+#ifdef CONFIG_DPP2
+       if (auth->akm == DPP_AKM_SAE) {
+#ifdef CONFIG_SAE
+               struct wpa_driver_capa capa;
+               int res;
+
+               res = wpa_drv_get_capa(wpa_s, &capa);
+               if (res == 0 &&
+                   !(capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE) &&
+                   !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "DPP: SAE not supported by the driver");
+                       return NULL;
+               }
+#else /* CONFIG_SAE */
+               wpa_printf(MSG_DEBUG, "DPP: SAE not supported in the build");
+               return NULL;
+#endif /* CONFIG_SAE */
+       }
+#endif /* CONFIG_DPP2 */
+
        ssid = wpa_config_add_network(wpa_s->conf);
        if (!ssid)
                return NULL;
@@ -1236,17 +1280,17 @@ fail:
 }
 
 
-static void wpas_dpp_process_config(struct wpa_supplicant *wpa_s,
-                                   struct dpp_authentication *auth)
+static int wpas_dpp_process_config(struct wpa_supplicant *wpa_s,
+                                  struct dpp_authentication *auth)
 {
        struct wpa_ssid *ssid;
 
        if (wpa_s->conf->dpp_config_processing < 1)
-               return;
+               return 0;
 
        ssid = wpas_dpp_add_network(wpa_s, auth);
        if (!ssid)
-               return;
+               return -1;
 
        wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_NETWORK_ID "%d", ssid->id);
        if (wpa_s->conf->dpp_config_processing == 2)
@@ -1259,20 +1303,24 @@ static void wpas_dpp_process_config(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_NO_CONFIG_WRITE */
 
        if (wpa_s->conf->dpp_config_processing < 2)
-               return;
+               return 0;
 
-       wpa_printf(MSG_DEBUG, "DPP: Trying to connect to the new network");
-       wpa_s->disconnected = 0;
-       wpa_s->reassociate = 1;
-       wpa_s->scan_runs = 0;
-       wpa_s->normal_scans = 0;
-       wpa_supplicant_cancel_sched_scan(wpa_s);
-       wpa_supplicant_req_scan(wpa_s, 0, 0);
+#ifdef CONFIG_DPP2
+       if (auth->peer_version >= 2) {
+               wpa_printf(MSG_DEBUG,
+                          "DPP: Postpone connection attempt to wait for completion of DPP Configuration Result");
+               auth->connect_on_tx_status = 1;
+               return 0;
+       }
+#endif /* CONFIG_DPP2 */
+
+       wpas_dpp_try_to_connect(wpa_s);
+       return 0;
 }
 
 
-static void wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s,
-                                      struct dpp_authentication *auth)
+static int wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s,
+                                     struct dpp_authentication *auth)
 {
        wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
        if (auth->ssid_len)
@@ -1324,7 +1372,7 @@ static void wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s,
                }
        }
 
-       wpas_dpp_process_config(wpa_s, auth);
+       return wpas_dpp_process_config(wpa_s, auth);
 }
 
 
@@ -1336,6 +1384,8 @@ static void wpas_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
        struct wpa_supplicant *wpa_s = ctx;
        const u8 *pos;
        struct dpp_authentication *auth = wpa_s->dpp_auth;
+       int res;
+       enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED;
 
        wpa_s->dpp_gas_dialog_token = -1;
 
@@ -1373,13 +1423,40 @@ static void wpas_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
                goto fail;
        }
 
-       wpas_dpp_handle_config_obj(wpa_s, auth);
-       dpp_auth_deinit(wpa_s->dpp_auth);
-       wpa_s->dpp_auth = NULL;
-       return;
+       res = wpas_dpp_handle_config_obj(wpa_s, auth);
+       if (res < 0)
+               goto fail;
 
+       status = DPP_STATUS_OK;
 fail:
-       wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
+       if (status != DPP_STATUS_OK)
+               wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
+#ifdef CONFIG_DPP2
+       if (auth->peer_version >= 2 &&
+           auth->conf_resp_status == DPP_STATUS_OK) {
+               struct wpabuf *msg;
+
+               wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
+               msg = dpp_build_conf_result(auth, status);
+               if (!msg)
+                       goto fail2;
+
+               wpa_msg(wpa_s, MSG_INFO,
+                       DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+                       MAC2STR(addr), auth->curr_freq,
+                       DPP_PA_CONFIGURATION_RESULT);
+               offchannel_send_action(wpa_s, auth->curr_freq,
+                                      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 */
+               return;
+       }
+fail2:
+#endif /* CONFIG_DPP2 */
        dpp_auth_deinit(wpa_s->dpp_auth);
        wpa_s->dpp_auth = NULL;
 }
@@ -1562,6 +1639,62 @@ static void wpas_dpp_rx_auth_conf(struct wpa_supplicant *wpa_s, const u8 *src,
 }
 
 
+#ifdef CONFIG_DPP2
+
+static void wpas_dpp_config_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_conf_result)
+               return;
+
+       wpa_printf(MSG_DEBUG,
+                  "DPP: Timeout while waiting for Configuration Result");
+       wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
+       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)
+{
+       struct dpp_authentication *auth = wpa_s->dpp_auth;
+       enum dpp_status_error status;
+
+       wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR,
+                  MAC2STR(src));
+
+       if (!auth || !auth->waiting_conf_result) {
+               wpa_printf(MSG_DEBUG,
+                          "DPP: No DPP Configuration waiting for result - drop");
+               return;
+       }
+
+       if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+                          MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+               return;
+       }
+
+       status = dpp_conf_result_rx(auth, hdr, buf, len);
+
+       offchannel_send_action_done(wpa_s);
+       wpas_dpp_listen_stop(wpa_s);
+       if (status == DPP_STATUS_OK)
+               wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT);
+       else
+               wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED);
+       dpp_auth_deinit(auth);
+       wpa_s->dpp_auth = NULL;
+       eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout, wpa_s, NULL);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
 static void wpas_dpp_rx_peer_disc_resp(struct wpa_supplicant *wpa_s,
                                       const u8 *src,
                                       const u8 *buf, size_t len)
@@ -2105,6 +2238,11 @@ void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
                wpas_dpp_rx_pkex_commit_reveal_resp(wpa_s, src, hdr, buf, len,
                                                    freq);
                break;
+#ifdef CONFIG_DPP2
+       case DPP_PA_CONFIGURATION_RESULT:
+               wpas_dpp_rx_conf_result(wpa_s, src, hdr, buf, len);
+               break;
+#endif /* CONFIG_DPP2 */
        default:
                wpa_printf(MSG_DEBUG,
                           "DPP: Ignored unsupported frame subtype %d", type);
@@ -2174,6 +2312,21 @@ wpas_dpp_gas_status_handler(void *ctx, struct wpabuf *resp, int ok)
                   ok);
        eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
        eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL);
+#ifdef CONFIG_DPP2
+       if (ok && auth->peer_version >= 2 &&
+           auth->conf_resp_status == DPP_STATUS_OK) {
+               wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
+               auth->waiting_conf_result = 1;
+               auth->conf_resp = NULL;
+               wpabuf_free(resp);
+               eloop_cancel_timeout(wpas_dpp_config_result_wait_timeout,
+                                    wpa_s, NULL);
+               eloop_register_timeout(2, 0,
+                                      wpas_dpp_config_result_wait_timeout,
+                                      wpa_s, NULL);
+               return;
+       }
+#endif /* CONFIG_DPP2 */
        offchannel_send_action_done(wpa_s);
        wpas_dpp_listen_stop(wpa_s);
        if (ok)
@@ -2286,10 +2439,8 @@ int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd)
        curve = get_param(cmd, " curve=");
        wpas_dpp_set_testing_options(wpa_s, auth);
        if (wpas_dpp_set_configurator(wpa_s, auth, cmd) == 0 &&
-           dpp_configurator_own_config(auth, curve, 0) == 0) {
-               wpas_dpp_handle_config_obj(wpa_s, auth);
-               ret = 0;
-       }
+           dpp_configurator_own_config(auth, curve, 0) == 0)
+               ret = wpas_dpp_handle_config_obj(wpa_s, auth);
 
        dpp_auth_deinit(auth);
        os_free(curve);
@@ -2610,6 +2761,9 @@ void wpas_dpp_deinit(struct wpa_supplicant *wpa_s)
        eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
        eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
        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);
+#endif /* CONFIG_DPP2 */
        offchannel_send_action_done(wpa_s);
        wpas_dpp_listen_stop(wpa_s);
        dpp_bootstrap_del(wpa_s, 0);