]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
DPP2: Connection status result (Enrollee)
authorJouni Malinen <jouni@codeaurora.org>
Sun, 15 Sep 2019 13:19:45 +0000 (16:19 +0300)
committerJouni Malinen <j@w1.fi>
Mon, 16 Sep 2019 14:25:34 +0000 (17:25 +0300)
Add support for reporting connection status after provisioning if the
Configurator requests this.

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

index ca84bea91612aff0bb0f8d58d60b2d99757125df..d35c82efb85e94fa3bf9c4fb77b41eaec2779535 100644 (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 */
 
 
index fa3fd76a097aa96d6796dbda46b2f1ec0c5b04bb..d560f4b06c9dee70b5bfb3f631fcf517ff4fc611 100644 (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);
index b9f46708c117557e3493dd5f8428650e68e6c516..020a6d02292153aa638209212593d0112725e871 100644 (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 */
index 9ba315f55254b80f122df614577d41ba80b67df8..b33798265c1739655ed489c8917bf33fcd1de7e1 100644 (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 */
index f28e4399ebf3b1f19ef4460ed93a6187b1170562..2e083fe4ee96f95c5fa2769df5d15e7c33b62ab2 100644 (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 ||
index 709a33fa8826ac605f604739ec742887f9d6ff6e..6fb4efb5f5212bbe2a4c5970296a0c0db3dd961d 100644 (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);
 }
index 32f390fd9864b89854661171f6004c409e91c382..872c19c8427ad201f651c7e4394f40bb2b94f344 100644 (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 */