diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 0389875b1..c715743f3 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -155,6 +155,7 @@ extern "C" { #define DPP_EVENT_CONNECTOR "DPP-CONNECTOR " #define DPP_EVENT_C_SIGN_KEY "DPP-C-SIGN-KEY " #define DPP_EVENT_NET_ACCESS_KEY "DPP-NET-ACCESS-KEY " +#define DPP_EVENT_MISSING_CONNECTOR "DPP-MISSING-CONNECTOR " /* MESH events */ #define MESH_GROUP_STARTED "MESH-GROUP-STARTED " diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c index 848621a9a..bd0ffa4d9 100644 --- a/wpa_supplicant/dpp_supplicant.c +++ b/wpa_supplicant/dpp_supplicant.c @@ -13,10 +13,15 @@ #include "common/dpp.h" #include "common/gas.h" #include "common/gas_server.h" +#include "rsn_supp/wpa.h" +#include "rsn_supp/pmksa_cache.h" #include "wpa_supplicant_i.h" +#include "config.h" #include "driver_i.h" #include "offchannel.h" #include "gas_query.h" +#include "bss.h" +#include "scan.h" #include "dpp_supplicant.h" @@ -1079,6 +1084,108 @@ static void wpas_dpp_rx_auth_conf(struct wpa_supplicant *wpa_s, const u8 *src, } +static void wpas_dpp_rx_peer_disc_resp(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *buf, size_t len) +{ + struct wpa_ssid *ssid; + const u8 *connector, *pk_hash, *nk_hash; + u16 connector_len, pk_hash_len, nk_hash_len; + struct dpp_introduction intro; + struct rsn_pmksa_cache_entry *entry; + + wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Response from " MACSTR, + MAC2STR(src)); + if (is_zero_ether_addr(wpa_s->dpp_intro_bssid) || + os_memcmp(src, wpa_s->dpp_intro_bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: Not waiting for response from " + MACSTR " - drop", MAC2STR(src)); + return; + } + offchannel_send_action_done(wpa_s); + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid == wpa_s->dpp_intro_network) + break; + } + if (!ssid || !ssid->dpp_connector || !ssid->dpp_netaccesskey || + !ssid->dpp_csign) { + wpa_printf(MSG_DEBUG, + "DPP: Profile not found for network introduction"); + return; + } + + connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len); + if (!connector) { + wpa_printf(MSG_DEBUG, + "DPP: Peer did not include its Connector"); + return; + } + + if (dpp_peer_intro(&intro, ssid->dpp_connector, + ssid->dpp_netaccesskey, + ssid->dpp_netaccesskey_len, + ssid->dpp_csign, + ssid->dpp_csign_len, + connector, connector_len) < 0) { + wpa_printf(MSG_INFO, + "DPP: Network Introduction protocol resulted in failure"); + goto fail; + } + + pk_hash = dpp_get_attr(buf, len, DPP_ATTR_PEER_NET_PK_HASH, + &pk_hash_len); + if (!pk_hash || pk_hash_len != SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, "DPP: Peer did not include SHA256(PK)"); + goto fail; + } + if (os_memcmp(pk_hash, intro.nk_hash, SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: SHA256(PK) mismatch"); + wpa_hexdump(MSG_DEBUG, "DPP: Received SHA256(PK)", + pk_hash, pk_hash_len); + wpa_hexdump(MSG_DEBUG, "DPP: Calculated SHA256(PK)", + intro.nk_hash, SHA256_MAC_LEN); + goto fail; + } + + nk_hash = dpp_get_attr(buf, len, DPP_ATTR_OWN_NET_NK_HASH, + &nk_hash_len); + if (!nk_hash || nk_hash_len != SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, "DPP: Peer did not include SHA256(NK)"); + goto fail; + } + if (os_memcmp(nk_hash, intro.pk_hash, SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: SHA256(NK) mismatch"); + wpa_hexdump(MSG_DEBUG, "DPP: Received SHA256(NK)", + nk_hash, nk_hash_len); + wpa_hexdump(MSG_DEBUG, "DPP: Calculated SHA256(NK)", + intro.pk_hash, SHA256_MAC_LEN); + goto fail; + } + + entry = os_zalloc(sizeof(*entry)); + if (!entry) + goto fail; + os_memcpy(entry->aa, src, ETH_ALEN); + os_memcpy(entry->pmkid, intro.pmkid, PMKID_LEN); + os_memcpy(entry->pmk, intro.pmk, intro.pmk_len); + entry->pmk_len = intro.pmk_len; + entry->akmp = WPA_KEY_MGMT_DPP; + /* TODO: expiration */ + entry->network_ctx = ssid; + wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry); + + wpa_printf(MSG_DEBUG, + "DPP: Try connection again after successful network introduction"); + if (wpa_supplicant_fast_associate(wpa_s) != 1) { + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } +fail: + os_memset(&intro, 0, sizeof(intro)); +} + + void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *buf, size_t len, unsigned int freq) { @@ -1108,6 +1215,9 @@ void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, case DPP_PA_AUTHENTICATION_CONF: wpas_dpp_rx_auth_conf(wpa_s, src, buf, len); break; + case DPP_PA_PEER_DISCOVERY_RESP: + wpas_dpp_rx_peer_disc_resp(wpa_s, src, buf, len); + break; default: wpa_printf(MSG_DEBUG, "DPP: Ignored unsupported frame subtype %d", type); @@ -1265,6 +1375,92 @@ int wpas_dpp_configurator_remove(struct wpa_supplicant *wpa_s, const char *id) } +static void +wpas_dpp_tx_introduction_status(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result result) +{ + wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR + " result=%s (DPP Peer Discovery Request)", + freq, MAC2STR(dst), + result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" : + (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" : + "FAILED")); + /* TODO: Time out wait for response more quickly in error cases? */ +} + + +int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_bss *bss) +{ + struct os_time now; + struct wpabuf *msg; + unsigned int wait_time; + + if (!(ssid->key_mgmt & WPA_KEY_MGMT_DPP) || !bss) + return 0; /* Not using DPP AKM - continue */ + if (wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid)) + return 0; /* PMKSA exists for DPP AKM - continue */ + + if (!ssid->dpp_connector || !ssid->dpp_netaccesskey || + !ssid->dpp_csign) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_MISSING_CONNECTOR + "missing %s", + !ssid->dpp_connector ? "Connector" : + (!ssid->dpp_netaccesskey ? "netAccessKey" : + "C-sign-key")); + return -1; + } + + os_get_time(&now); + + if (ssid->dpp_csign_expiry && ssid->dpp_csign_expiry < now.sec) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_MISSING_CONNECTOR + "C-sign-key expired"); + return -1; + } + + if (ssid->dpp_netaccesskey_expiry && + ssid->dpp_netaccesskey_expiry < now.sec) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_MISSING_CONNECTOR + "netAccessKey expired"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "DPP: Starting network introduction protocol to derive PMKSA for " + MACSTR, MAC2STR(bss->bssid)); + + msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_REQ, + 4 + os_strlen(ssid->dpp_connector)); + if (!msg) + return -1; + + /* DPP Connector */ + wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR); + wpabuf_put_le16(msg, os_strlen(ssid->dpp_connector)); + wpabuf_put_str(msg, ssid->dpp_connector); + + /* TODO: Timeout on AP response */ + wait_time = wpa_s->max_remain_on_chan; + if (wait_time > 2000) + wait_time = 2000; + offchannel_send_action(wpa_s, bss->freq, bss->bssid, wpa_s->own_addr, + broadcast, + wpabuf_head(msg), wpabuf_len(msg), + wait_time, wpas_dpp_tx_introduction_status, 0); + wpabuf_free(msg); + + /* Request this connection attempt to terminate - new one will be + * started when network introduction protocol completes */ + os_memcpy(wpa_s->dpp_intro_bssid, bss->bssid, ETH_ALEN); + wpa_s->dpp_intro_network = ssid; + return 1; +} + + int wpas_dpp_init(struct wpa_supplicant *wpa_s) { u8 adv_proto_id[7]; @@ -1308,4 +1504,5 @@ void wpas_dpp_deinit(struct wpa_supplicant *wpa_s) dpp_configurator_del(wpa_s, 0); dpp_auth_deinit(wpa_s->dpp_auth); wpa_s->dpp_auth = NULL; + os_memset(wpa_s->dpp_intro_bssid, 0, ETH_ALEN); } diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h index 403f436e3..6748f7c01 100644 --- a/wpa_supplicant/dpp_supplicant.h +++ b/wpa_supplicant/dpp_supplicant.h @@ -27,5 +27,7 @@ int wpas_dpp_configurator_add(struct wpa_supplicant *wpa_s, const char *cmd); int wpas_dpp_configurator_remove(struct wpa_supplicant *wpa_s, const char *id); int wpas_dpp_init(struct wpa_supplicant *wpa_s); 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); #endif /* DPP_SUPPLICANT_H */ diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index d16108faa..314d8f85e 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1215,9 +1215,10 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, #ifdef CONFIG_DPP if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) && - !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid)) { - /* TODO: Go through DPP network introduction to generate - * PMKSA entry. */ + !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid) && + (!ssid->dpp_connector || + !ssid->dpp_netaccesskey || + !ssid->dpp_csign)) { if (debug_print) wpa_dbg(wpa_s, MSG_DEBUG, " skip - no PMKSA entry for DPP"); diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index ca6f179a1..f599c7e8b 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1847,6 +1847,11 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, return; } +#ifdef CONFIG_DPP + if (wpas_dpp_check_connect(wpa_s, ssid, bss) != 0) + return; +#endif /* CONFIG_DPP */ + #ifdef CONFIG_TDLS if (bss) wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1), diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 20cc71769..156f800c2 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1171,6 +1171,8 @@ struct wpa_supplicant { int dpp_netrole_ap; int dpp_auth_ok_on_ack; int dpp_gas_client; + u8 dpp_intro_bssid[ETH_ALEN]; + void *dpp_intro_network; #ifdef CONFIG_TESTING_OPTIONS char *dpp_config_obj_override; char *dpp_discovery_override;