Add P2P2 support for GO negotiation wrapped in PASN authentication frames as a action wrapper attribute. Signed-off-by: Shivani Baranwal <quic_shivbara@xxxxxxxxxxx> diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 9af586a7b..090bb2655 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -2418,7 +2418,8 @@ static void pasn_fils_auth_resp(struct hostapd_data *hapd, wpabuf_head(pasn->secret), wpabuf_len(pasn->secret), pasn_get_ptk(sta->pasn), pasn_get_akmp(sta->pasn), - pasn_get_cipher(sta->pasn), sta->pasn->kdk_len); + pasn_get_cipher(sta->pasn), sta->pasn->kdk_len, + sta->pasn->kek_len); if (ret) { wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PTK"); goto fail; diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c index a95ae36dc..5763c51f4 100644 --- a/src/common/common_module_tests.c +++ b/src/common/common_module_tests.c @@ -651,7 +651,7 @@ static int pasn_test_pasn_auth(void) spa_addr, bssid, dhss, sizeof(dhss), &ptk, WPA_KEY_MGMT_PASN, WPA_CIPHER_CCMP, - WPA_KDK_MAX_LEN); + WPA_KDK_MAX_LEN, 0); if (ret) return ret; diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index c753d49e7..71a812c7f 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -396,6 +396,10 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, elems->mbssid_known_bss = pos; elems->mbssid_known_bss_len = elen; break; + case WLAN_EID_EXT_PASN_ENCRYPTED_ELEMENT: + elems->pasn_encrypted_ie = pos; + elems->pasn_encrypted_ie_len = elen; + break; default: if (show_errors) { wpa_printf(MSG_MSGDUMP, diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 32c6b08b5..66dc5f900 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -66,6 +66,7 @@ struct ieee802_11_elems { const u8 *vendor_vht; const u8 *p2p; const u8 *p2p2_ie; + const u8 *pasn_encrypted_ie; const u8 *wfd; const u8 *link_id; const u8 *interworking; @@ -138,6 +139,7 @@ struct ieee802_11_elems { u8 vendor_vht_len; u8 p2p_len; u8 p2p2_ie_len; + u8 pasn_encrypted_ie_len; u8 wfd_len; u8 interworking_len; u8 qos_map_set_len; diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index ee45b124f..7e3233d95 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -524,6 +524,7 @@ #define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110 #define WLAN_EID_EXT_QOS_CHARACTERISTICS 113 #define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114 +#define WLAN_EID_EXT_PASN_ENCRYPTED_ELEMENT 140 /* Extended Capabilities field */ #define WLAN_EXT_CAPAB_20_40_COEX 0 @@ -616,6 +617,7 @@ #define WLAN_RSNX_CAPAB_SECURE_RTT 9 #define WLAN_RSNX_CAPAB_URNM_MFPR_X20 10 #define WLAN_RSNX_CAPAB_URNM_MFPR 15 +#define WLAN_RSNX_CAPAB_KEK 18 #define WLAN_RSNX_CAPAB_SSID_PROTECTION 21 /* Multiple BSSID element subelements */ diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 94ce43dde..b7438cea9 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -1460,9 +1460,9 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *bssid, const u8 *dhss, size_t dhss_len, struct wpa_ptk *ptk, int akmp, int cipher, - size_t kdk_len) + size_t kdk_len, size_t kek_len) { - u8 tmp[WPA_KCK_MAX_LEN + WPA_TK_MAX_LEN + WPA_KDK_MAX_LEN]; + u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN + WPA_KDK_MAX_LEN]; u8 *data; size_t data_len, ptk_len; int ret = -1; @@ -1497,7 +1497,7 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, ptk->kck_len = WPA_PASN_KCK_LEN; ptk->tk_len = wpa_cipher_key_len(cipher); ptk->kdk_len = kdk_len; - ptk->kek_len = 0; + ptk->kek_len = kek_len; ptk->kek2_len = 0; ptk->kck2_len = 0; @@ -1508,7 +1508,7 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, goto err; } - ptk_len = ptk->kck_len + ptk->tk_len + ptk->kdk_len; + ptk_len = ptk->kck_len + ptk->tk_len + ptk->kdk_len + ptk->kek_len; if (ptk_len > sizeof(tmp)) goto err; @@ -1537,12 +1537,18 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, os_memcpy(ptk->kck, tmp, WPA_PASN_KCK_LEN); wpa_hexdump_key(MSG_DEBUG, "PASN: KCK:", ptk->kck, WPA_PASN_KCK_LEN); - os_memcpy(ptk->tk, tmp + WPA_PASN_KCK_LEN, ptk->tk_len); + if (kek_len) { + os_memcpy(ptk->kek, tmp + WPA_PASN_KCK_LEN, ptk->kek_len); + wpa_hexdump_key(MSG_DEBUG, "PASN: KEK:", + ptk->kek, ptk->kek_len); + } + + os_memcpy(ptk->tk, tmp + WPA_PASN_KCK_LEN + ptk->kek_len, ptk->tk_len); wpa_hexdump_key(MSG_DEBUG, "PASN: TK:", ptk->tk, ptk->tk_len); if (kdk_len) { - os_memcpy(ptk->kdk, tmp + WPA_PASN_KCK_LEN + ptk->tk_len, - ptk->kdk_len); + os_memcpy(ptk->kdk, tmp + WPA_PASN_KCK_LEN + ptk->kek_len + + ptk->tk_len, ptk->kdk_len); wpa_hexdump_key(MSG_DEBUG, "PASN: KDK:", ptk->kdk, ptk->kdk_len); } diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 1e3136843..249f1d1c1 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -248,6 +248,7 @@ struct wpa_eapol_key { #define WPA_PASN_KCK_LEN 32 #define WPA_PASN_MIC_MAX_LEN 24 #define WPA_LTF_KEYSEED_MAX_LEN 48 +#define WPA_KEK_128 16 /** * struct wpa_ptk - WPA Pairwise Transient Key @@ -751,7 +752,7 @@ int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *bssid, const u8 *dhss, size_t dhss_len, struct wpa_ptk *ptk, int akmp, int cipher, - size_t kdk_len); + size_t kdk_len, size_t kek_len); u8 pasn_mic_len(int akmp, int cipher); diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 5e12402e7..cd7817ecd 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -15,12 +15,15 @@ #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "crypto/sha256.h" +#include "crypto/sha384.h" #include "crypto/crypto.h" #include "crypto/random.h" #include "wps/wps_i.h" #include "p2p_i.h" #include "p2p.h" - +#include "common/sae.h" +#include "pasn/pasn_common.h" +#include "crypto/aes_wrap.h" static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx); static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev); @@ -243,6 +246,9 @@ void p2p_go_neg_failed(struct p2p_data *p2p, int status) peer->go_neg_conf = NULL; p2p->go_neg_peer = NULL; + if (peer->p2p2 && peer->pasn) + wpa_pasn_reset(peer->pasn); + os_memset(&res, 0, sizeof(res)); res.status = status; os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN); @@ -960,6 +966,12 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev) dev->bootstrap_params = NULL; } + if (dev->pasn) { + wpa_pasn_reset(dev->pasn); + pasn_data_deinit(dev->pasn); + dev->pasn = NULL; + } + wpabuf_free(dev->info.wfd_subelems); wpabuf_free(dev->info.vendor_elems); wpabuf_free(dev->go_neg_conf); @@ -1912,6 +1924,9 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) wpabuf_free(peer->go_neg_conf); peer->go_neg_conf = NULL; + if (peer->p2p2 && peer->pasn) + wpa_pasn_reset(peer->pasn); + p2p_set_state(p2p, P2P_PROVISIONING); p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); } @@ -1928,17 +1943,16 @@ static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa, switch (data[0]) { case P2P_GO_NEG_REQ: - p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq); + p2p_handle_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_GO_NEG_RESP: - p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq); + p2p_handle_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_GO_NEG_CONF: - p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1); + p2p_handle_go_neg_conf(p2p, sa, data + 1, len - 1, false); break; case P2P_INVITATION_REQ: - p2p_process_invitation_req(p2p, sa, data + 1, len - 1, - rx_freq); + p2p_handle_invitation_req(p2p, sa, data + 1, len - 1, rx_freq); break; case P2P_INVITATION_RESP: p2p_process_invitation_resp(p2p, sa, data + 1, len - 1); @@ -3016,6 +3030,9 @@ int p2p_pairing_info_init(struct p2p_data *p2p) pairing_info->dev_ik.dik_len); p2p->pairing_info = pairing_info; + p2p->initiator_pmksa = pasn_initiator_pmksa_cache_init(); + p2p->responder_pmksa = pasn_responder_pmksa_cache_init(); + return 0; } @@ -3085,6 +3102,8 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) void p2p_pairing_info_deinit(struct p2p_data *p2p) { + pasn_initiator_pmksa_cache_deinit(p2p->initiator_pmksa); + pasn_responder_pmksa_cache_deinit(p2p->responder_pmksa); os_free(p2p->pairing_info); } @@ -5860,3 +5879,745 @@ void p2p_process_usd_elems(struct p2p_data *p2p, const u8 *ies, u16 ies_len, p2p_parse_free(&msg); } + + +int p2p_prepare_pasn_extra_ie(struct p2p_data *p2p, struct wpabuf *extra_ies, + struct wpabuf *frame, bool dira) +{ + struct wpabuf *buf, *buf2; + + buf = wpabuf_alloc(1500); + if (!buf) { + p2p_dbg(p2p, "Mem alloc failed for buf"); + return -1; + } + + /* P2P Capability Extension attribute */ + p2p_buf_add_pcea(buf, p2p); + + /* P2P Device Identity Resolution attribute */ + if (dira) + p2p_buf_add_dira(buf, p2p); + + if (frame) { + p2p_dbg(p2p, "P2P: Added Action frame wrapper"); + wpabuf_put_u8(buf, P2P_ATTR_ACTION_FRAME_WRAPPER); + wpabuf_put_le16(buf, wpabuf_len(frame)); + wpabuf_put_buf(buf, frame); + } + + buf2 = p2p_encaps_p2p_vendor_ie(p2p, buf, P2P2_IE_VENDOR_TYPE); + wpabuf_free(buf); + + wpabuf_put_buf(extra_ies, buf2); + wpabuf_free(buf2); + + return 0; +} + +struct wpabuf *p2p_pairing_generate_rsnxe(int akmp) +{ + u32 capab; + size_t flen = 0; + struct wpabuf *buf; + + capab = BIT(WLAN_RSNX_CAPAB_KEK); + + if (akmp == WPA_KEY_MGMT_SAE) + capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); + + while (capab >> flen * 8) + flen++; + + buf = wpabuf_alloc(2 + flen); + if (!buf) { + wpa_printf(MSG_ERROR, "Memory allocation failed"); + return NULL; + } + + if (wpabuf_tailroom(buf) < 2 + flen) { + wpa_printf(MSG_ERROR, "wpabuf tail room small"); + wpabuf_free(buf); + return NULL; + } + capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */ + + wpa_printf(MSG_DEBUG, "RSNXE capabilities: %04x", capab); + wpabuf_put_u8(buf, WLAN_EID_RSNX); + wpabuf_put_u8(buf, flen); + while (flen--) { + wpabuf_put_u8(buf, (capab & 0xff)); + capab = capab >> 8; + } + return buf; +} + +/* sae password id to derive pt */ +#define P2P_PAIRING_SSID "516F9A020000" + +void p2p_pairing_set_password(struct pasn_data *pasn, const char *passphrase, + u32 len) +{ + const u8 *pairing_ssid; + size_t pairing_ssid_len; + + if (!passphrase) { + wpa_printf(MSG_ERROR, "p2p pairing password NULL"); + return; + } + + pairing_ssid = (const u8 *)(P2P_PAIRING_SSID); + pairing_ssid_len = strlen(P2P_PAIRING_SSID); + pasn->pt = sae_derive_pt(NULL, pairing_ssid, pairing_ssid_len, + (const u8 *)passphrase, len, NULL); + /* Set passpharse for Pairing Responder to validate PASN auth1 frame*/ + pasn->password = passphrase; +} + +void p2p_pasn_initialize(struct p2p_data *p2p, struct p2p_device *dev, + const u8 *addr, int freq) +{ + struct pasn_data *pasn; + struct wpabuf *rsnxe; + + if (!p2p || !dev) + return; + + if (dev->pasn) + wpa_pasn_reset(dev->pasn); + else + dev->pasn = pasn_data_init(); + + pasn = dev->pasn; + + os_memcpy(pasn->own_addr, p2p->cfg->dev_addr, ETH_ALEN); + os_memcpy(pasn->peer_addr, addr, ETH_ALEN); + + if (dev->role == P2P_ROLE_PAIRING_INITIATOR) + memcpy(pasn->bssid, pasn->peer_addr, ETH_ALEN); + else + memcpy(pasn->bssid, pasn->own_addr, ETH_ALEN); + + pasn->noauth = 1; + pasn->group = 19; + pasn->cipher = WPA_CIPHER_CCMP; + pasn->kek_len = WPA_KEK_128; + + if (dev->password_len) { + pasn->akmp = WPA_KEY_MGMT_SAE; + p2p_pairing_set_password(pasn, dev->password, + dev->password_len); + pasn->rsnxe_capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); + } else { + pasn->akmp = WPA_KEY_MGMT_PASN; + } + + pasn->rsn_pairwise = pasn->cipher; + pasn->wpa_key_mgmt = pasn->akmp; + + rsnxe = p2p_pairing_generate_rsnxe(pasn->akmp); + if (rsnxe) { + pasn->rsnxe_ie = os_zalloc(wpabuf_len(rsnxe)); + if (!pasn->rsnxe_ie) { + p2p_dbg(p2p, "Mem alloc failed for pasn rsnxe ie"); + wpabuf_free(rsnxe); + return; + } + os_memcpy((u8 *)pasn->rsnxe_ie, wpabuf_head_u8(rsnxe), + wpabuf_len(rsnxe)); + pasn->rsnxe_ie_len = wpabuf_len(rsnxe); + wpabuf_free(rsnxe); + } + + if (dev->role == P2P_ROLE_PAIRING_INITIATOR) + pasn->pmksa = p2p->initiator_pmksa; + else + pasn->pmksa = p2p->responder_pmksa; + + pasn->cb_ctx = p2p->cfg->cb_ctx; + pasn->send_mgmt = p2p->cfg->pasn_send_mgmt; + pasn->update_extra_ies = p2p->cfg->pasn_update_extra_ies; + pasn->parse_encrypted_data = p2p->cfg->pasn_parse_encrypted_data; + + pasn->freq = freq; +} + + + + +int p2p_initiate_pasn_auth(struct p2p_data *p2p, const u8 *addr, int freq) +{ + struct pasn_data *pasn; + struct p2p_device *dev; + struct wpabuf *extra_ies, *req; + int ret = 0; + + if (!addr) { + p2p_dbg(p2p, "peer address NULL"); + return -1; + } + + dev = p2p_get_device(p2p, addr); + if (!dev) { + p2p_dbg(p2p, "Peer not known"); + return -1; + } + + dev->role = P2P_ROLE_PAIRING_INITIATOR; + p2p_pasn_initialize(p2p, dev, addr, freq); + pasn = dev->pasn; + + /* FIXME: Added to resolve listen freq issue resulting in GO Neg no + * common channel failure + */ + p2p->cfg->reg_class = p2p->op_reg_class; + p2p->cfg->channel = p2p->op_channel; + + req = p2p_build_go_neg_req(p2p, dev); + if (!req) + return -1; + + p2p->go_neg_peer = dev; + dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE; + + extra_ies = wpabuf_alloc(1500); + if (!extra_ies) { + wpabuf_free(req); + p2p_dbg(p2p, "Mem alloc failed for extra ies"); + return -1; + } + + if (p2p_prepare_pasn_extra_ie(p2p, extra_ies, req, false)) { + p2p_dbg(p2p, "prepare pasn extra ies failed"); + ret = -1; + goto out; + } + + pasn->extra_ies = os_zalloc(wpabuf_len(extra_ies)); + if (!pasn->extra_ies) { + p2p_dbg(p2p, "Mem alloc failed for pasn extra ies"); + ret = -1; + goto out; + } + + os_memcpy((u8 *)pasn->extra_ies, wpabuf_head_u8(extra_ies), + wpabuf_len(extra_ies)); + pasn->extra_ies_len = wpabuf_len(extra_ies); + + /* Start PASN Auth */ + if (wpas_pasn_start(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid, + pasn->akmp, pasn->cipher, pasn->group, pasn->freq, + NULL, 0, NULL, 0, NULL)) { + p2p_dbg(p2p, "p2p pasn start failed"); + ret = -1; + } +out: + if (pasn->extra_ies) { + os_free((u8 *)pasn->extra_ies); + pasn->extra_ies = NULL; + pasn->extra_ies_len = 0; + } + wpabuf_free(req); + wpabuf_free(extra_ies); + return ret; +} + + +int p2p_pasn_handle_action_wrapper(struct p2p_data *p2p, + struct p2p_device *dev, + const struct ieee80211_mgmt *mgmt, + size_t len, int freq, int trans_seq) +{ + const u8 *ies; + size_t ies_len; + size_t data_len = 0; + const u8 *data = NULL; + struct p2p_message msg; + + ies = mgmt->u.auth.variable; + ies_len = len - offsetof(struct ieee80211_mgmt, u.auth.variable); + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ies, ies_len, &msg)) { + p2p_dbg(p2p, "Failed to parse P2P IE from auth frame"); + p2p_parse_free(&msg); + return -1; + } + + if (msg.dira && msg.dira_len) { + } + + if (msg.action_frame_wrapper && msg.action_frame_wrapper_len) { + data = msg.action_frame_wrapper; + data_len = msg.action_frame_wrapper_len; + if (data[0] == WLAN_ACTION_PUBLIC && + data[1] == WLAN_PA_VENDOR_SPECIFIC) { + data += 2; + data_len -= 2; + if (data_len < 4 || + WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE) { + p2p_parse_free(&msg); + return -1; + } + data += 4; + data_len -= 4; + } else { + p2p_dbg(p2p, "Invalid category in action frame wrapper in Auth %d", + trans_seq); + p2p_parse_free(&msg); + return -1; + } + } + + if (trans_seq == 1) { + if (msg.dira && data && data[0] == P2P_INVITATION_REQ) { + p2p_process_invitation_req(p2p, mgmt->sa, data + 1, + data_len - 1, freq); + if (!p2p->invitation_resp) + p2p_dbg(p2p, "No Invitation Response found"); + dev->pasn->action_frame_wrapper = p2p->invitation_resp; + } else if (data && data[0] == P2P_GO_NEG_REQ) { + p2p_process_go_neg_req(p2p, mgmt->sa, data + 1, + data_len - 1, freq, true); + if (!p2p->go_neg_resp) + p2p_dbg(p2p, "No GO Neg Response found"); + dev->pasn->action_frame_wrapper = p2p->go_neg_resp; + } else { + p2p_dbg(p2p, "Invalid action frame wrapper in Auth1"); + } + } else if (trans_seq == 2) { + if (msg.dira && data && data[0] == P2P_INVITATION_RESP) { + p2p_process_invitation_resp(p2p, mgmt->sa, data + 1, + data_len - 1); + dev->pasn->action_frame_wrapper = NULL; + } else if (data && data[0] == P2P_GO_NEG_RESP) { + p2p_process_go_neg_resp(p2p, mgmt->sa, data + 1, + data_len - 1, freq, true); + if (!p2p->go_neg_conf) + p2p_dbg(p2p, "No GO Neg confirm found"); + dev->pasn->action_frame_wrapper = p2p->go_neg_conf; + } else { + p2p_dbg(p2p, "Invalid action frame wrapper in Auth2"); + } + } else if (trans_seq == 3) { + if (data && data[0] == P2P_GO_NEG_CONF) { + p2p_handle_go_neg_conf(p2p, mgmt->sa, data + 1, + data_len - 1, true); + } else { + p2p_invitation_resp_cb(p2p, P2P_SEND_ACTION_SUCCESS); + } + } + p2p_parse_free(&msg); + return 0; +} + + +static void p2p_pasn_add_encrypted_element(struct p2p_data *p2p, + struct p2p_device *dev, + struct wpabuf *buf) +{ + int ret; + struct pasn_data *pasn; + struct wpabuf *p2p2_ie; + u8 *len, *dika_len, *p2p2_ie_len; + u8 *pos, *key_data, *encrypted_data; + u16 key_data_len, pad_len = 0; + + if (!p2p || !dev || !dev->pasn) + return; + + pasn = dev->pasn; + + if (dev->req_bootstrap_method != P2P_PBMA_OPPORTUNISTIC && + !p2p->pairing_info->enable_pairing_cache) + return; + + p2p2_ie = wpabuf_alloc(100); + if (!p2p2_ie) { + p2p_dbg(p2p, "Mem alloc failed for p2p2 IE"); + return; + } + + p2p2_ie_len = p2p_buf_add_p2p2_ie_hdr(p2p2_ie); + + if (p2p->pairing_info->enable_pairing_cache) { + wpabuf_put_u8(p2p2_ie, P2P_ATTR_DEVICE_IDENTITY_KEY); + dika_len = wpabuf_put(p2p2_ie, 2); + + wpabuf_put_u8(p2p2_ie, DIRA_CIPHER_VERSION_128); + wpabuf_put_data(p2p2_ie, p2p->pairing_info->dev_ik.dik_data, + p2p->pairing_info->dev_ik.dik_len); + wpabuf_put_be32(p2p2_ie, p2p->pairing_info->dev_ik.expiration); + + WPA_PUT_LE16(dika_len, + (u8 *)wpabuf_put(p2p2_ie, 0) - dika_len - 2); + } + + if (dev->req_bootstrap_method != P2P_PBMA_OPPORTUNISTIC && + dev->dev_password_len) { + wpabuf_put_u8(p2p2_ie, P2P_ATTR_PASSWORD); + wpabuf_put_le16(p2p2_ie, dev->dev_password_len); + wpabuf_put_data(p2p2_ie, dev->dev_password, + dev->dev_password_len); + } + + p2p_buf_update_p2p2_ie_hdr(p2p2_ie, p2p2_ie_len); + + key_data = (u8 *)wpabuf_head(p2p2_ie); + key_data_len = wpabuf_len(p2p2_ie); + + pad_len = key_data_len % 8; + + if (pad_len) { + pad_len = 8 - pad_len; + pos = key_data + key_data_len; + *pos++ = 0xdd; + } + key_data_len += pad_len + 8; + + encrypted_data = os_malloc(key_data_len); + if (!encrypted_data) { + p2p_dbg(p2p, "P2P PASN: Mem alloc failed for encrypted data"); + wpabuf_free(p2p2_ie); + return; + } + ret = aes_wrap(pasn->ptk.kek, pasn->ptk.kek_len, + (key_data_len - 8) / 8, key_data, encrypted_data); + if (ret) { + p2p_dbg(p2p, "P2P PASN: AES upwrap failed, ret=%d", ret); + goto out; + } + + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + len = wpabuf_put(buf, 1); + + wpabuf_put_u8(buf, WLAN_EID_EXT_PASN_ENCRYPTED_ELEMENT); + + wpabuf_put_data(buf, encrypted_data, key_data_len); + *len = (u8 *)wpabuf_put(buf, 0) - len - 1; + +out: + os_free(encrypted_data); + wpabuf_free(p2p2_ie); +} + + +int p2p_pasn_update_extra_ies(struct p2p_data *p2p, const u8 *peer_addr, + bool dira) +{ + int ret = -1; + struct p2p_device *dev; + struct pasn_data *pasn; + struct wpabuf *extra_ies; + + if (!p2p) + return -1; + + dev = p2p_get_device(p2p, (u8 *)peer_addr); + if (!dev || !dev->pasn) { + p2p_dbg(p2p, "P2P PASN: Peer not found" MACSTR, + MAC2STR(peer_addr)); + return -1; + } + pasn = dev->pasn; + + extra_ies = wpabuf_alloc(1500); + if (!extra_ies) { + p2p_dbg(p2p, "Mem alloc failed for extra ies"); + goto out; + } + + if (p2p_prepare_pasn_extra_ie(p2p, extra_ies, + pasn->action_frame_wrapper, dira)) { + p2p_dbg(p2p, "prepare pasn extra ies failed"); + goto out; + } + + p2p_pasn_add_encrypted_element(p2p, dev, extra_ies); + + pasn->extra_ies = os_zalloc(wpabuf_len(extra_ies)); + if (!pasn->extra_ies) { + p2p_dbg(p2p, "Mem alloc failed for pasn extra ies"); + goto out; + } + + os_memcpy((u8 *)pasn->extra_ies, wpabuf_head_u8(extra_ies), + wpabuf_len(extra_ies)); + pasn->extra_ies_len = wpabuf_len(extra_ies); + ret = 0; + +out: + wpabuf_free(extra_ies); + wpabuf_free(pasn->action_frame_wrapper); + pasn->action_frame_wrapper = NULL; + + return ret; +} + + +int p2p_pasn_parse_encrypted_data(struct p2p_data *p2p, const u8 *data, + size_t len) +{ + int ret = -1; + u8 attr_id; + u8 *buf, *pos; + u16 rem_len, attr_len; + struct p2p_device *dev; + struct pasn_data *pasn; + struct ieee802_11_elems elems; + const struct ieee80211_mgmt *mgmt = + (const struct ieee80211_mgmt *) data; + + if (!p2p) + return -1; + + dev = p2p_get_device(p2p, (u8 *)mgmt->sa); + if (!dev || !dev->pasn) { + p2p_dbg(p2p, "P2P PASN: Peer not found" MACSTR, + MAC2STR(mgmt->sa)); + return -1; + } + + if (ieee802_11_parse_elems(mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable), + &elems, 0) == ParseFailed) { + p2p_dbg(p2p, "P2P PASN: Failed parsing Authentication frame"); + return -1; + } + + if (!elems.pasn_encrypted_ie || !elems.pasn_encrypted_ie_len) { + p2p_dbg(p2p, "P2P PASN: No encrypted IEs"); + return 0; + } + + pasn = dev->pasn; + rem_len = elems.pasn_encrypted_ie_len; + + buf = os_zalloc(rem_len); + if (!buf) { + p2p_dbg(p2p, "Mem alloc failed for buf"); + return -1; + } + + ret = aes_unwrap(pasn->ptk.kek, pasn->ptk.kek_len, (rem_len - 8) / 8, + elems.pasn_encrypted_ie, buf); + if (ret) { + p2p_dbg(p2p, "P2P PASN: AES unwrap failed, ret=%d", ret); + goto done; + } + + pos = buf; + if (pos[0] != WLAN_EID_VENDOR_SPECIFIC || + WPA_GET_BE32(&pos[2]) != P2P2_IE_VENDOR_TYPE) { + p2p_dbg(p2p, "P2P PASN: P2P2 IE not present"); + goto done; + } + + pos += 6; + rem_len -= 6; + + while (rem_len > 2) { + attr_id = *pos++; + attr_len = WPA_GET_LE16(pos); + + pos += 2; + rem_len -= 3; + switch (attr_id) { + case P2P_ATTR_DEVICE_IDENTITY_KEY: + if (rem_len < 13) { + p2p_dbg(p2p, "P2P PASN: Invalid rem len %d", rem_len); + goto done; + } + dev->info.dik_cipher_version = *pos++; + rem_len--; + if (dev->info.dik_cipher_version == 0) { + memcpy(dev->info.device_identity_key, pos, 16); + pos += 16; + rem_len -= 16; + } else { + p2p_dbg(p2p, "P2P PASN: Invalid cipher"); + goto done; + } + dev->info.dik_lifetime = WPA_GET_BE32(pos); + pos += 4; + rem_len -= 4; + break; + case P2P_ATTR_PASSWORD: + if (rem_len < 1) { + p2p_dbg(p2p, "P2P PASN: Invalid rem len %d", rem_len); + goto done; + } + dev->info.password_len = attr_len; + memset(dev->info.password, 0, + sizeof(dev->info.password)); + memcpy(dev->info.password, pos, attr_len); + break; + default: + p2p_dbg(p2p, "Invalid Attr ID: %d", attr_id); + break; + } + } + ret = 0; +done: + os_free(buf); + return ret; +} + +int p2p_pasn_auth_tx_status(struct p2p_data *p2p, const u8 *data, + size_t data_len, u8 acked) +{ + int ret = 0; + struct p2p_device *dev; + struct pasn_data *pasn; + const struct ieee80211_mgmt *mgmt = + (const struct ieee80211_mgmt *) data; + + if (!p2p) + return -1; + + dev = p2p_get_device(p2p, (u8 *)mgmt->da); + if (!dev || !dev->pasn) { + p2p_dbg(p2p, "P2P PASN: Peer not found" MACSTR, + MAC2STR(mgmt->da)); + return -1; + } + + pasn = dev->pasn; + + ret = wpa_pasn_auth_tx_status(pasn, data, data_len, acked); + if (ret != 1 && acked == 0 && pasn->frame) { + return pasn->send_mgmt(pasn->cb_ctx, wpabuf_head(pasn->frame), + wpabuf_len(pasn->frame), 0, pasn->freq, + 1000); + } else if (pasn->frame) { + wpabuf_free(pasn->frame); + pasn->frame = NULL; + } + + if (ret != 1) + return ret; + + if (dev == p2p->go_neg_peer) + p2p_go_complete(p2p, dev); + + return 0; +} + +int p2p_handle_pasn_auth(struct p2p_data *p2p, struct p2p_device *dev, + const struct ieee80211_mgmt *mgmt, size_t len, + int freq) +{ + struct pasn_data *pasn; + u16 auth_alg, auth_transaction, status_code; + + if (!p2p || !dev || !dev->pasn) + return -1; + + if (os_memcmp(mgmt->da, p2p->cfg->dev_addr, ETH_ALEN) != 0) { + p2p_dbg(p2p, "P2P PASN Responder: Not our frame"); + return -1; + } + + pasn = dev->pasn; + auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + status_code = le_to_host16(mgmt->u.auth.status_code); + + auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + + if (status_code != WLAN_STATUS_SUCCESS && + status_code != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) { + p2p_dbg(p2p, "P2P PASN: Authentication rejected - status=%u", + status_code); + return -1; + } + + if (auth_alg != WLAN_AUTH_PASN || auth_transaction == 2) { + p2p_dbg(p2p, "P2P PASN Responder: Not PASN frame " + " or Unexpected auth frame, auth_alg = %d", + auth_alg); + return -1; + } + if (auth_transaction == 1) { + if (p2p_pasn_handle_action_wrapper(p2p, dev, mgmt, len, freq, + auth_transaction)) { + p2p_dbg(p2p, "P2P PASN Responder: Handle Auth1 action wrapper failed"); + return -1; + } + if (handle_auth_pasn_1(pasn, p2p->cfg->dev_addr, mgmt->sa, mgmt, + len) < 0) { + p2p_dbg(p2p, "P2P PASN Responder: Handle Auth1 failed"); + return -1; + } + } else if (auth_transaction == 3) { + if (handle_auth_pasn_3(pasn, p2p->cfg->dev_addr, mgmt->sa, mgmt, + len) < 0) { + p2p_dbg(p2p, "P2P PASN Responder: Handle PASN Auth3 failed"); + return -1; + } + if (p2p_pasn_handle_action_wrapper(p2p, dev, mgmt, len, freq, + auth_transaction)) { + p2p_dbg(p2p, "P2P PASN Responder: Handle Auth3 action wrapper failed"); + memset(dev->info.device_identity_key, 0, + sizeof(dev->info.device_identity_key)); + memset(dev->info.password, 0, + sizeof(dev->info.password)); + dev->info.password_len = 0; + return -1; + } + } + return 0; +} + + +int p2p_pasn_auth_rx(struct p2p_data *p2p, const struct ieee80211_mgmt *mgmt, + size_t len, int freq) +{ + int ret = 0; + u8 auth_transaction; + struct p2p_device *dev; + struct pasn_data *pasn; + struct wpa_pasn_params_data pasn_data; + + dev = p2p_get_device(p2p, (u8 *)mgmt->sa); + if (!dev) { + p2p_dbg(p2p, "P2P PASN: Peer not found" MACSTR, + MAC2STR(mgmt->sa)); + return -1; + } + + if (!dev->pasn) { + p2p_dbg(p2p, "P2P PASN: uninitialized"); + return -1; + } + + pasn = dev->pasn; + + if (pasn->frame) { + wpabuf_free(pasn->frame); + pasn->frame = NULL; + } + + pasn_register_callbacks(pasn, p2p->cfg->cb_ctx, + p2p->cfg->pasn_send_mgmt, NULL); + auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + + if (dev->role == P2P_ROLE_PAIRING_INITIATOR && auth_transaction == 2) { + if (p2p_pasn_handle_action_wrapper(p2p, dev, mgmt, len, freq, + auth_transaction)) { + p2p_dbg(p2p, "P2P PASN Initiator: Handle Auth2 action wrapper failed"); + return -1; + } + ret = wpa_pasn_auth_rx(pasn, (const u8 *)mgmt, len, &pasn_data); + forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk)); + + if (ret < 0) { + p2p_dbg(p2p, "P2P PASN: wpa_pasn_auth_rx failed"); + dev->role = P2P_ROLE_IDLE; + } + + } else { + ret = p2p_handle_pasn_auth(p2p, dev, mgmt, len, freq); + } + return ret; +} diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index bf73adb70..1403d0d20 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -460,6 +460,31 @@ struct p2p_peer_info { * p2p_pairing_config - P2P Pairing configuration */ struct p2p_pairing_config pairing_config; + + /** + * cipher version for Device Identity key generation + */ + u8 dik_cipher_version; + + /** + * Device Identity key which is unique for a device + */ + u8 device_identity_key[128]; + + /** + * Device Identity key lifetime + */ + u32 dik_lifetime; + + /** + * password used during group formation post opportunistic pasn auth + */ + char password[100]; + + /** + * password length. Non zero if valid + */ + u16 password_len; }; enum p2p_prov_disc_status { @@ -1255,6 +1280,23 @@ struct p2p_config { */ void (*bootstrap_completed)(void *ctx, const u8 *addr, int status, int freq); + + /** + * pasn_send_mgmt - Function handler to transmit a Management frame + * @ctx: Callback context from cb_ctx + * @data : Frame to transmit + * @data_len: Length of frame to transmit + * @noack : No ack flag + * @freq: Frequency in MHz for the channel on which to transmit + * @wait: How many milliseconds to wait for a response frame + * Returns: 0 on success, -1 on failure + */ + int (*pasn_send_mgmt)(void *ctx, const u8 *data, size_t data_len, + int noack, unsigned int freq, unsigned int wait); + + int (*pasn_update_extra_ies)(void *ctx, const u8 *peer_addr, bool dira); + + int (*pasn_parse_encrypted_data)(void *ctx, const u8 *data, size_t len); }; @@ -2557,4 +2599,13 @@ int p2p_channel_to_freq(int op_class, int channel); struct wpabuf * p2p_usd_elems(struct p2p_data *p2p); void p2p_process_usd_elems(struct p2p_data *p2p, const u8 *ies, u16 ies_len, const u8 *peer_addr, unsigned int freq); +int p2p_initiate_pasn_auth(struct p2p_data *p2p, const u8 *addr, int freq); +int p2p_pasn_auth_rx(struct p2p_data *p2p, const struct ieee80211_mgmt *mgmt, + size_t len, int freq); +int p2p_pasn_update_extra_ies(struct p2p_data *p2p, const u8 *peer_addr, + bool dira); +int p2p_pasn_parse_encrypted_data(struct p2p_data *p2p, const u8 *data, + size_t len); +int p2p_pasn_auth_tx_status(struct p2p_data *p2p, const u8 *data, + size_t data_len, u8 acked); #endif /* P2P_H */ diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c index fd48702fe..64e6b2aa3 100644 --- a/src/p2p/p2p_build.c +++ b/src/p2p/p2p_build.c @@ -1017,3 +1017,38 @@ int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, return 0; } + + +struct wpabuf *p2p_encaps_p2p_vendor_ie(struct p2p_data *p2p, + struct wpabuf *subelems, u32 ie_type) +{ + struct wpabuf *ie; + const u8 *pos, *end; + size_t len; + + if (!subelems) + return NULL; + + len = wpabuf_len(subelems) + 1000; + + ie = wpabuf_alloc(len); + if (!ie) + return NULL; + + pos = wpabuf_head(subelems); + end = pos + wpabuf_len(subelems); + + while (end > pos) { + size_t frag_len = end - pos; + + if (frag_len > 251) + frag_len = 251; + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(ie, 4 + frag_len); + wpabuf_put_be32(ie, ie_type); + wpabuf_put_data(ie, pos, frag_len); + pos += frag_len; + } + + return ie; +} diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 04e5139d6..84f39d290 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -135,15 +135,14 @@ static const char * p2p_wps_method_str(enum p2p_wps_method wps_method) } -static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, - struct p2p_device *peer) +struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, + struct p2p_device *peer) { - struct wpabuf *buf; - u8 *len; u8 group_capab; size_t extra = 0; u16 pw_id; bool is_6ghz_capab; + struct wpabuf *buf, *buf2, *p2p_ie; #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) @@ -153,13 +152,16 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]); - buf = wpabuf_alloc(1000 + extra); - if (buf == NULL) + buf2 = wpabuf_alloc(1000 + extra); + if (!buf2) return NULL; - p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token); + p2p_buf_add_public_action_hdr(buf2, P2P_GO_NEG_REQ, peer->dialog_token); + + p2p_ie = wpabuf_alloc(500); + if (!p2p_ie) + return NULL; - len = p2p_buf_add_ie_hdr(buf); group_capab = 0; if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; @@ -170,17 +172,17 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; - p2p_buf_add_capability(buf, p2p->dev_capab & + p2p_buf_add_capability(p2p_ie, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, group_capab); - p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | peer->tie_breaker); - p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); - p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class, + p2p_buf_add_go_intent(p2p_ie, (p2p->go_intent << 1) | peer->tie_breaker); + p2p_buf_add_config_timeout(p2p_ie, p2p->go_timeout, p2p->client_timeout); + p2p_buf_add_listen_channel(p2p_ie, p2p->cfg->country, p2p->cfg->reg_class, p2p->cfg->channel); if (p2p->ext_listen_interval) - p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period, + p2p_buf_add_ext_listen_timing(p2p_ie, p2p->ext_listen_period, p2p->ext_listen_interval); - p2p_buf_add_intended_addr(buf, p2p->intended_addr); + p2p_buf_add_intended_addr(p2p_ie, p2p->intended_addr); is_6ghz_capab = is_p2p_6ghz_capable(p2p) && p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr); if (p2p->num_pref_freq) { @@ -191,37 +193,41 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, p2p->num_pref_freq, &pref_chanlist, go); p2p_channels_dump(p2p, "channel list after filtering", &pref_chanlist); - p2p_buf_add_channel_list(buf, p2p->cfg->country, + p2p_buf_add_channel_list(p2p_ie, p2p->cfg->country, &pref_chanlist, is_6ghz_capab); } else { - p2p_buf_add_channel_list(buf, p2p->cfg->country, + p2p_buf_add_channel_list(p2p_ie, p2p->cfg->country, &p2p->channels, is_6ghz_capab); } - p2p_buf_add_device_info(buf, p2p, peer); - p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p_buf_add_device_info(p2p_ie, p2p, peer); + p2p_buf_add_operating_channel(p2p_ie, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); - p2p_buf_update_ie_hdr(buf, len); - p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list, + buf = p2p_encaps_p2p_vendor_ie(p2p, p2p_ie, P2P_IE_VENDOR_TYPE); + wpabuf_free(p2p_ie); + + p2p_buf_add_pref_channel_list(buf2, p2p->pref_freq_list, p2p->num_pref_freq); /* WPS IE with Device Password ID attribute */ pw_id = p2p_wps_method_pw_id(peer->wps_method); if (peer->oob_pw_id) pw_id = peer->oob_pw_id; - if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { + if (peer && !peer->p2p2 && p2p_build_wps_ie(p2p, buf2, pw_id, 0) < 0) { p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request"); + wpabuf_free(buf2); wpabuf_free(buf); return NULL; } #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) - wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); + wpabuf_put_buf(buf2, p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]) - wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]); + wpabuf_put_buf(buf2, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]); + buf = wpabuf_concat(buf2, buf); return buf; } @@ -292,13 +298,12 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, u8 dialog_token, u8 status, u8 tie_breaker) { - struct wpabuf *buf; - u8 *len; u8 group_capab; size_t extra = 0; u16 pw_id; bool is_6ghz_capab; struct p2p_channels pref_chanlist; + struct wpabuf *buf, *buf2, *p2p_ie; p2p_dbg(p2p, "Building GO Negotiation Response"); @@ -310,14 +315,17 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]); - buf = wpabuf_alloc(1000 + extra); - if (buf == NULL) + buf2 = wpabuf_alloc(1000 + extra); + if (!buf2) return NULL; - p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_RESP, dialog_token); + p2p_buf_add_public_action_hdr(buf2, P2P_GO_NEG_RESP, dialog_token); - len = p2p_buf_add_ie_hdr(buf); - p2p_buf_add_status(buf, status); + p2p_ie = wpabuf_alloc(500); + if (!p2p_ie) + return NULL; + + p2p_buf_add_status(p2p_ie, status); group_capab = 0; if (peer && peer->go_state == LOCAL_GO) { if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { @@ -331,24 +339,25 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; } - p2p_buf_add_capability(buf, p2p->dev_capab & + p2p_buf_add_capability(p2p_ie, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, group_capab); - p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); - p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); + p2p_buf_add_go_intent(p2p_ie, (p2p->go_intent << 1) | tie_breaker); + p2p_buf_add_config_timeout(p2p_ie, p2p->go_timeout, p2p->client_timeout); if (p2p->override_pref_op_class) { p2p_dbg(p2p, "Override operating channel preference"); - p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p_buf_add_operating_channel(p2p_ie, p2p->cfg->country, p2p->override_pref_op_class, p2p->override_pref_channel); } else if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) { p2p_dbg(p2p, "Omit Operating Channel attribute"); } else { - p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p_buf_add_operating_channel(p2p_ie, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); } - p2p_buf_add_intended_addr(buf, p2p->intended_addr); + p2p_buf_add_intended_addr(p2p_ie, p2p->intended_addr); + if (p2p->num_pref_freq) { bool go = (peer && peer->go_state == LOCAL_GO) || p2p->go_intent == 15; @@ -362,12 +371,12 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, p2p->allow_6ghz); } if (status || peer == NULL) { - p2p_buf_add_channel_list(buf, p2p->cfg->country, + p2p_buf_add_channel_list(p2p_ie, p2p->cfg->country, &pref_chanlist, false); } else if (peer->go_state == REMOTE_GO) { is_6ghz_capab = is_p2p_6ghz_capable(p2p) && p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr); - p2p_buf_add_channel_list(buf, p2p->cfg->country, + p2p_buf_add_channel_list(p2p_ie, p2p->cfg->country, &pref_chanlist, is_6ghz_capab); } else { struct p2p_channels res; @@ -376,33 +385,37 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr); p2p_channels_intersect(&pref_chanlist, &peer->channels, &res); - p2p_buf_add_channel_list(buf, p2p->cfg->country, &res, - is_6ghz_capab); + p2p_buf_add_channel_list(p2p_ie, p2p->cfg->country, &res, + is_6ghz_capab); } - p2p_buf_add_device_info(buf, p2p, peer); + p2p_buf_add_device_info(p2p_ie, p2p, peer); if (peer && peer->go_state == LOCAL_GO) { - p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid, + p2p_buf_add_group_id(p2p_ie, p2p->cfg->dev_addr, p2p->ssid, p2p->ssid_len); } - p2p_buf_update_ie_hdr(buf, len); + + buf = p2p_encaps_p2p_vendor_ie(p2p, p2p_ie, P2P_IE_VENDOR_TYPE); + wpabuf_free(p2p_ie); /* WPS IE with Device Password ID attribute */ pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY); if (peer && peer->oob_pw_id) pw_id = peer->oob_pw_id; - if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { + if (peer && !peer->p2p2 && p2p_build_wps_ie(p2p, buf2, pw_id, 0) < 0) { p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response"); + wpabuf_free(buf2); wpabuf_free(buf); return NULL; } #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) - wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); + wpabuf_put_buf(buf2, p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]) - wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]); + wpabuf_put_buf(buf2, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]); + buf = wpabuf_concat(buf2, buf); return buf; } @@ -801,21 +814,19 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, } -void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len, int rx_freq) +int p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq, bool p2p2) { struct p2p_device *dev = NULL; - struct wpabuf *resp; struct p2p_message msg; u8 status = P2P_SC_FAIL_INVALID_PARAMS; int tie_breaker = 0; - int freq; p2p_dbg(p2p, "Received GO Negotiation Request from " MACSTR "(freq=%d)", MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) - return; + return -1; if (!msg.capability) { p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Request"); @@ -890,7 +901,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_go_neg_failed(p2p, *msg.status); p2p_parse_free(&msg); - return; + return -1; } goto fail; } @@ -922,7 +933,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "User has rejected this peer"); status = P2P_SC_FAIL_REJECTED_BY_USER; } else if (dev == NULL || - (dev->wps_method == WPS_NOT_READY && + (dev->wps_method == WPS_NOT_READY && !p2p2 && (p2p->authorized_oob_dev_pw_id == 0 || p2p->authorized_oob_dev_pw_id != msg.dev_password_id))) { @@ -968,7 +979,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) { p2p_dbg(p2p, "Do not reply since peer has higher address and GO Neg Request already sent"); p2p_parse_free(&msg); - return; + return -1; } if (dev->go_neg_req_sent && @@ -976,7 +987,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Do not reply since peer is waiting for us to start a new GO Negotiation and GO Neg Request already sent"); p2p_parse_free(&msg); - return; + return -1; } go = p2p_go_det(p2p->go_intent, *msg.go_intent); @@ -993,6 +1004,9 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, goto fail; } + if (p2p2) + goto skip; + switch (msg.dev_password_id) { case DEV_PW_REGISTRAR_SPECIFIED: p2p_dbg(p2p, "PIN from peer Display"); @@ -1059,7 +1073,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; } - +skip: if (go && p2p_go_select_channel(p2p, dev, &status) < 0) goto fail; @@ -1086,7 +1100,10 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, p2p_set_state(p2p, P2P_GO_NEG); p2p_clear_timeout(p2p); dev->dialog_token = msg.dialog_token; - os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); + if (!is_zero_ether_addr(msg.intended_addr)) { + p2p_dbg(p2p, "msg.intended_addr" MACSTR, MAC2STR(msg.intended_addr)); + os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); + } p2p->go_neg_peer = dev; eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); status = P2P_SC_SUCCESS; @@ -1095,22 +1112,13 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, fail: if (dev) dev->status = status; - resp = p2p_build_go_neg_resp(p2p, dev, msg.dialog_token, status, - !tie_breaker); + p2p->go_neg_resp = p2p_build_go_neg_resp(p2p, dev, msg.dialog_token, + status, !tie_breaker); + p2p_parse_free(&msg); - if (resp == NULL) - return; - p2p_dbg(p2p, "Sending GO Negotiation Response"); - if (rx_freq > 0) - freq = rx_freq; - else - freq = p2p_channel_to_freq(p2p->cfg->reg_class, - p2p->cfg->channel); - if (freq < 0) { - p2p_dbg(p2p, "Unknown regulatory class/channel"); - wpabuf_free(resp); - return; - } + if (!p2p->go_neg_resp) + return -1; + if (status == P2P_SC_SUCCESS) { p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE; dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM; @@ -1128,13 +1136,38 @@ fail: } else p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE_FAILURE; - if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, - p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 100) < 0) { - p2p_dbg(p2p, "Failed to send Action frame"); + return 0; +} + +void p2p_handle_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, + size_t len, int rx_freq) +{ + int freq; + + if (p2p_process_go_neg_req(p2p, sa, data, len, rx_freq, false)) + return; + + p2p_dbg(p2p, "Sending GO Negotiation Response"); + + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Unknown regulatory class/channel"); + return; } - wpabuf_free(resp); + if (p2p->go_neg_resp && + p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, wpabuf_head(p2p->go_neg_resp), + wpabuf_len(p2p->go_neg_resp), 100) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); + } + wpabuf_free(p2p->go_neg_resp); + p2p->go_neg_resp = NULL; + return; } @@ -1143,12 +1176,11 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, u8 dialog_token, u8 status, const u8 *resp_chan, int go) { - struct wpabuf *buf; - u8 *len; struct p2p_channels res; u8 group_capab; size_t extra = 0; bool is_6ghz_capab; + struct wpabuf *buf, *buf2, *p2p_ie; p2p_dbg(p2p, "Building GO Negotiation Confirm"); @@ -1160,14 +1192,17 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]) extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]); - buf = wpabuf_alloc(1000 + extra); - if (buf == NULL) + buf2 = wpabuf_alloc(1000 + extra); + if (!buf2) return NULL; - p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_CONF, dialog_token); + p2p_buf_add_public_action_hdr(buf2, P2P_GO_NEG_CONF, dialog_token); - len = p2p_buf_add_ie_hdr(buf); - p2p_buf_add_status(buf, status); + p2p_ie = wpabuf_alloc(500); + if (!p2p_ie) + return NULL; + + p2p_buf_add_status(p2p_ie, status); group_capab = 0; if (peer->go_state == LOCAL_GO) { if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { @@ -1181,40 +1216,44 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, if (p2p->cfg->p2p_intra_bss) group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; } - p2p_buf_add_capability(buf, p2p->dev_capab & + p2p_buf_add_capability(p2p_ie, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, group_capab); if (go || resp_chan == NULL) - p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p_buf_add_operating_channel(p2p_ie, p2p->cfg->country, p2p->op_reg_class, p2p->op_channel); else - p2p_buf_add_operating_channel(buf, (const char *) resp_chan, + p2p_buf_add_operating_channel(p2p_ie, (const char *) resp_chan, resp_chan[3], resp_chan[4]); p2p_channels_intersect(&p2p->channels, &peer->channels, &res); is_6ghz_capab = is_p2p_6ghz_capable(p2p) && p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr); - p2p_buf_add_channel_list(buf, p2p->cfg->country, &res, is_6ghz_capab); + p2p_buf_add_channel_list(p2p_ie, p2p->cfg->country, &res, is_6ghz_capab); if (go) { - p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid, + p2p_buf_add_group_id(p2p_ie, p2p->cfg->dev_addr, p2p->ssid, p2p->ssid_len); } - p2p_buf_update_ie_hdr(buf, len); + + buf = p2p_encaps_p2p_vendor_ie(p2p, p2p_ie, P2P_IE_VENDOR_TYPE); + wpabuf_free(p2p_ie); #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) - wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); + wpabuf_put_buf(buf2, p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]) - wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]); + wpabuf_put_buf(buf2, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]); + + buf = wpabuf_concat(buf2, buf); return buf; } -void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len, int rx_freq) +int p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, + size_t len, int rx_freq, bool p2p2) { struct p2p_device *dev; int go = -1; @@ -1225,20 +1264,20 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Received GO Negotiation Response from " MACSTR " (freq=%d)", MAC2STR(sa), rx_freq); dev = p2p_get_device(p2p, sa); - if (dev == NULL || dev->wps_method == WPS_NOT_READY || + if (dev == NULL || (!p2p2 && dev->wps_method == WPS_NOT_READY) || dev != p2p->go_neg_peer) { p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); - return; + return -1; } if (p2p_parse(data, len, &msg)) - return; + return -1; if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) { p2p_dbg(p2p, "Was not expecting GO Negotiation Response - ignore"); p2p_parse_free(&msg); - return; + return -1; } dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; p2p_update_peer_6ghz_capab(dev, &msg); @@ -1247,7 +1286,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); - return; + return -1; } if (!msg.status) { @@ -1276,7 +1315,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_parse_free(&msg); - return; + return -1; } if (!msg.capability) { @@ -1377,6 +1416,9 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, } else dev->oper_freq = 0; + if (p2p2) + goto skip; + switch (msg.dev_password_id) { case DEV_PW_REGISTRAR_SPECIFIED: p2p_dbg(p2p, "PIN from peer Display"); @@ -1432,6 +1474,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, goto fail; } +skip: if (go && p2p_go_select_channel(p2p, dev, &status) < 0) goto fail; @@ -1446,7 +1489,10 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, p2p_clear_timeout(p2p); p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa)); - os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); + if (!is_zero_ether_addr(msg.intended_addr)) { + p2p_dbg(p2p, "msg.intended_addr" MACSTR, MAC2STR(msg.intended_addr)); + os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); + } fail: /* Store GO Negotiation Confirmation to allow retransmission */ @@ -1454,15 +1500,15 @@ fail: dev->go_neg_conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status, msg.operating_channel, go); + p2p->go_neg_conf = wpabuf_dup(dev->go_neg_conf); p2p_parse_free(&msg); - if (dev->go_neg_conf == NULL) - return; - p2p_dbg(p2p, "Sending GO Negotiation Confirm"); + if (status == P2P_SC_SUCCESS) { p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM; dev->go_state = go ? LOCAL_GO : REMOTE_GO; } else p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (rx_freq > 0) freq = rx_freq; else @@ -1471,6 +1517,36 @@ fail: dev->go_neg_conf_freq = freq; dev->go_neg_conf_sent = 0; + if (status != P2P_SC_SUCCESS) { + p2p_dbg(p2p, "GO Negotiation failed"); + p2p_go_neg_failed(p2p, status); + } + return 0; +} + +void p2p_handle_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, + size_t len, int rx_freq) +{ + int freq; + struct p2p_device *dev; + + dev = p2p_get_device(p2p, sa); + if (dev == NULL || dev->wps_method == WPS_NOT_READY || + dev != p2p->go_neg_peer) { + p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, + MAC2STR(sa)); + return; + } + + if (p2p_process_go_neg_resp(p2p, sa, data, len, rx_freq, false)) + return; + + p2p_dbg(p2p, "Sending GO Negotiation Confirm"); + if (rx_freq > 0) + freq = rx_freq; + else + freq = dev->listen_freq; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa, wpabuf_head(dev->go_neg_conf), wpabuf_len(dev->go_neg_conf), 50) < 0) { @@ -1479,15 +1555,16 @@ fail: p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } else dev->go_neg_conf_sent++; - if (status != P2P_SC_SUCCESS) { - p2p_dbg(p2p, "GO Negotiation failed"); - p2p_go_neg_failed(p2p, status); - } + + + wpabuf_free(p2p->go_neg_conf); + p2p->go_neg_conf = NULL; + return; } -void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len) +void p2p_handle_go_neg_conf(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, bool p2p2) { struct p2p_device *dev; struct p2p_message msg; @@ -1495,7 +1572,7 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Received GO Negotiation Confirm from " MACSTR, MAC2STR(sa)); dev = p2p_get_device(p2p, sa); - if (dev == NULL || dev->wps_method == WPS_NOT_READY || + if (dev == NULL || (!p2p2 && dev->wps_method == WPS_NOT_READY) || dev != p2p->go_neg_peer) { p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 5463f4ba2..ed893d438 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -42,6 +42,13 @@ enum p2p_go_state { }; +/* Enumeration for P2P device current role */ +enum p2p_role { + P2P_ROLE_IDLE = 0, + P2P_ROLE_PAIRING_INITIATOR, + P2P_ROLE_PAIRING_RESPONDER, +}; + /** * struct bootstrap_params - P2P Device bootstrap request params */ @@ -193,6 +200,22 @@ struct p2p_device { * password length. Non zero if valid */ u16 password_len; + + /* pasn data structure */ + struct pasn_data *pasn; + + /* device role */ + enum p2p_role role; + + /** + * password used during group formation post opportunistic pasn auth + */ + char dev_password[100]; + + /** + * password length. Non zero if valid + */ + u16 dev_password_len; }; struct p2p_sd_query { @@ -637,6 +660,29 @@ struct p2p_data { bool allow_6ghz; struct p2p_pairing_info *pairing_info; + /*p2p pairing initiator pmksa cache list */ + struct rsn_pmksa_cache *initiator_pmksa; + /* p2p pairing responder pmksa cache list */ + struct rsn_pmksa_cache *responder_pmksa; + /** + * go_neg_resp - GO Negotiation Response frame + */ + struct wpabuf *go_neg_resp; + + /** + * go_neg_conf - GO Negotiation Confirmation frame + */ + struct wpabuf *go_neg_conf; + + /** + * invitation_req - Invitation request frame + */ + struct wpabuf *invitation_req; + + /** + * invitation_resp - Invitation Response frame + */ + struct wpabuf *invitation_resp; }; /** @@ -749,6 +795,12 @@ struct p2p_message { const u8 *pbma_info; size_t pbma_info_len; + + const u8 *action_frame_wrapper; + size_t action_frame_wrapper_len; + + const u8 *dira; + size_t dira_len; }; @@ -889,6 +941,8 @@ int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, void p2p_buf_add_pref_channel_list(struct wpabuf *buf, const struct weighted_pcl *pref_freq_list, unsigned int size); +struct wpabuf *p2p_encaps_p2p_vendor_ie(struct p2p_data *p2p, + struct wpabuf *subelems, u32 ie_type); /* p2p_sd.c */ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, @@ -905,15 +959,21 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev); /* p2p_go_neg.c */ +struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, + struct p2p_device *peer); int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, struct p2p_device *dev, const u8 *channel_list, size_t channel_list_len); -void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len, int rx_freq); -void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len, int rx_freq); -void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len); +void p2p_handle_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, + size_t len, int rx_freq); +void p2p_handle_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, + size_t len, int rx_freq); +void p2p_handle_go_neg_conf(struct p2p_data *p2p, const u8 *sa, const u8 *data, + size_t len, bool p2p2); +int p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, + size_t len, int rx_freq, bool p2p2); +int p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, + size_t len, int rx_freq, bool p2p2); int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev); u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method); void p2p_reselect_channel(struct p2p_data *p2p, @@ -934,10 +994,14 @@ void p2p_process_pcea(struct p2p_data *p2p, struct p2p_message *msg, struct p2p_device *dev); /* p2p_invitation.c */ -void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len, int rx_freq); +void p2p_handle_invitation_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_handle_invitation_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len); +int p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len); + const u8 *data, size_t len); int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, const u8 *go_dev_addr, int dev_pw_id); void p2p_invitation_req_cb(struct p2p_data *p2p, int success); @@ -999,6 +1063,8 @@ void p2p_pref_channel_filter(const struct p2p_channels *a, struct p2p_channels *res, bool go); void p2p_sd_query_cb(struct p2p_data *p2p, int success); +void p2p_pasn_initialize(struct p2p_data *p2p, struct p2p_device *dev, + const u8 *addr, int freq); void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) PRINTF_FORMAT(2, 3); diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index 70a7f6fa6..8ade838a8 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -181,14 +181,12 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, } -void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, - const u8 *data, size_t len, int rx_freq) +int p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) { struct p2p_device *dev; struct p2p_message msg; - struct wpabuf *resp = NULL; u8 status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; - int freq; int go = 0; u8 group_bssid[ETH_ALEN], *bssid; int op_freq = 0; @@ -202,7 +200,7 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) - return; + return -1; dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { @@ -385,21 +383,11 @@ fail: bssid = group_bssid; else bssid = NULL; - resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status, - bssid, reg_class, channel, channels); - - if (resp == NULL) - goto out; - - if (rx_freq > 0) - freq = rx_freq; - else - freq = p2p_channel_to_freq(p2p->cfg->reg_class, - p2p->cfg->channel); - if (freq < 0) { - p2p_dbg(p2p, "Unknown regulatory class/channel"); - goto out; - } + p2p->invitation_resp = p2p_build_invitation_resp(p2p, dev, + msg.dialog_token, + status, bssid, + reg_class, channel, + channels); /* * Store copy of invitation data to be used when processing TX status @@ -424,17 +412,38 @@ fail: } p2p->inv_status = status; p2p->inv_op_freq = op_freq; + p2p_parse_free(&msg); + return 0; +} + + +void p2p_handle_invitation_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + int freq; + + if (p2p_process_invitation_req(p2p, sa, data, len, rx_freq)) + return; + + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) + p2p_dbg(p2p, "Unknown regulatory class/channel"); p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE; - if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, + if (p2p->invitation_resp && + p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 50) < 0) { + wpabuf_head(p2p->invitation_resp), + wpabuf_len(p2p->invitation_resp), 50) < 0) p2p_dbg(p2p, "Failed to send Action frame"); - } -out: - wpabuf_free(resp); - p2p_parse_free(&msg); + wpabuf_free(p2p->invitation_resp); + p2p->invitation_resp = NULL; + return; } diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c index a70e18079..de2a43fef 100644 --- a/src/p2p/p2p_parse.c +++ b/src/p2p/p2p_parse.c @@ -437,6 +437,26 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, msg->pbma_info_len = len; wpa_printf(MSG_DEBUG, "P2P: * PBMA (length=%u)", len); break; + case P2P_ATTR_ACTION_FRAME_WRAPPER: + if (len < 2) { + wpa_printf(MSG_DEBUG, "P2P: Too short Action frame (length %d)", + len); + return -1; + } + msg->action_frame_wrapper = data; + msg->action_frame_wrapper_len = len; + wpa_printf(MSG_DEBUG, "P2P: * Action frame wrapper (length=%u)", len); + break; + case P2P_ATTR_DEVICE_IDENTITY_RESOLUTION: + if (len < 2) { + wpa_printf(MSG_DEBUG, "P2P: Too short DIRA (length %d)", + len); + return -1; + } + msg->dira = data; + msg->dira_len = len; + wpa_printf(MSG_DEBUG, "P2P: * DIRA (length=%u)", len); + break; default: wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d " "(length %d)", id, len); diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c index 0d2903712..dc54be0a2 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -815,6 +815,10 @@ static void p2p_process_prov_disc_bootstrap_req(struct p2p_data *p2p, wpa_printf(MSG_ERROR, "Bootstrap received %d", bootstrap); + if (status == P2P_SC_SUCCESS) { + dev->role = P2P_ROLE_PAIRING_RESPONDER; + p2p_pasn_initialize(p2p, dev, sa, rx_freq); + } out: /* * Send PD Bootstrapping Response for the PD Request diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h index 36710c2b7..469a4d438 100644 --- a/src/pasn/pasn_common.h +++ b/src/pasn/pasn_common.h @@ -55,6 +55,7 @@ struct pasn_data { int rsn_pairwise; u16 rsnxe_capab; const u8 *rsnxe_ie; + size_t rsnxe_ie_len; bool custom_pmkid_valid; u8 custom_pmkid[PMKID_LEN]; @@ -66,6 +67,7 @@ struct pasn_data { size_t extra_ies_len; /* External modules do not access below variables */ + size_t kek_len; u16 group; bool secure_ltf; int freq; @@ -129,6 +131,8 @@ struct pasn_data { struct os_reltime last_comeback_key_update; u16 comeback_idx; u16 *comeback_pending_idx; + struct wpabuf *action_frame_wrapper; + struct wpabuf *frame; /** * send_mgmt - Function handler to transmit a Management frame @@ -150,6 +154,10 @@ struct pasn_data { */ int (*validate_custom_pmkid)(void *ctx, const u8 *addr, const u8 *pmkid); + + int (*update_extra_ies)(void *ctx, const u8 *peer_addr, bool dira); + + int (*parse_encrypted_data)(void *ctx, const u8 *data, size_t len); }; /* Initiator */ @@ -202,9 +210,14 @@ void pasn_set_peer_addr(struct pasn_data *pasn, const u8 *addr); void pasn_set_bssid(struct pasn_data *pasn, const u8 *addr); void pasn_set_initiator_pmksa(struct pasn_data *pasn, struct rsn_pmksa_cache *pmksa); -void pasn_set_responder_pmksa(struct pasn_data *pasn, - struct rsn_pmksa_cache *pmksa); int pasn_set_pt(struct pasn_data *pasn, struct sae_pt *pt); +struct rsn_pmksa_cache * pasn_initiator_pmksa_cache_init(void); +void pasn_initiator_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); +int pasn_initiator_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, u8 *own_addr, + u8 *bssid, u8 *pmk, size_t pmk_len); +int pasn_initiator_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, u8 *bssid, + u8 *pmkid, u8 *pmk, size_t *pmk_len); +void pasn_initiator_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa); /* Responder */ void pasn_set_password(struct pasn_data *pasn, const char *password); @@ -215,6 +228,15 @@ void pasn_set_rsnxe_ie(struct pasn_data *pasn, const u8 *rsnxe_ie); void pasn_set_custom_pmkid(struct pasn_data *pasn, const u8 *pmkid); int pasn_set_extra_ies(struct pasn_data *pasn, const u8 *extra_ies, size_t extra_ies_len); +void pasn_set_responder_pmksa(struct pasn_data *pasn, + struct rsn_pmksa_cache *pmksa); +struct rsn_pmksa_cache * pasn_responder_pmksa_cache_init(void); +void pasn_responder_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); +int pasn_responder_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, u8 *own_addr, + u8 *bssid, u8 *pmk, size_t pmk_len); +int pasn_responder_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, u8 *bssid, + u8 *pmkid, u8 *pmk, size_t *pmk_len); +void pasn_responder_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa); int pasn_get_akmp(struct pasn_data *pasn); int pasn_get_cipher(struct pasn_data *pasn); diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c index d273067b7..e56ef3897 100644 --- a/src/pasn/pasn_initiator.c +++ b/src/pasn/pasn_initiator.c @@ -26,6 +26,50 @@ #include "pasn_common.h" +struct rsn_pmksa_cache * pasn_initiator_pmksa_cache_init(void) +{ + return pmksa_cache_init(NULL, NULL, NULL, NULL, NULL); +} + + +void pasn_initiator_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +{ + return pmksa_cache_deinit(pmksa); +} + + +int pasn_initiator_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, u8 *own_addr, + u8 *bssid, u8 *pmk, size_t pmk_len) +{ + if (pmksa_cache_add(pmksa, pmk, pmk_len, NULL, NULL, 0, bssid, own_addr, + NULL, WPA_KEY_MGMT_SAE, 0)) + return 0; + return -1; +} + + +int pasn_initiator_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, u8 *bssid, + u8 *pmkid, u8 *pmk, size_t *pmk_len) +{ + struct rsn_pmksa_cache_entry *entry; + + entry = pmksa_cache_get(pmksa, bssid, NULL, NULL, NULL, 0); + if (entry) { + os_memcpy(pmkid, entry->pmkid, PMKID_LEN); + os_memcpy(pmk, entry->pmk, entry->pmk_len); + *pmk_len = entry->pmk_len; + return 0; + } + return -1; +} + + +void pasn_initiator_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa) +{ + return pmksa_cache_flush(pmksa, NULL, NULL, 0, false); +} + + void pasn_set_initiator_pmksa(struct pasn_data *pasn, struct rsn_pmksa_cache *pmksa) { @@ -587,7 +631,10 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn, if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0) goto fail; - wpa_pasn_add_rsnxe(buf, pasn->rsnxe_capab); + if (pasn->rsnxe_ie) + wpabuf_put_data(buf, pasn->rsnxe_ie, pasn->rsnxe_ie_len); + else + wpa_pasn_add_rsnxe(buf, pasn->rsnxe_capab); wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len); @@ -616,11 +663,13 @@ fail: } -static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn) +static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn, + const u8 *mgmt, size_t len) { struct wpabuf *buf, *wrapped_data_buf = NULL; u8 mic[WPA_PASN_MAX_MIC_LEN]; - u8 mic_len, data_len; + u8 mic_len; + size_t data_len; const u8 *data; u8 *ptr; u8 wrapped_data; @@ -654,6 +703,11 @@ static struct wpabuf * wpas_pasn_build_auth_3(struct pasn_data *pasn) wpabuf_free(wrapped_data_buf); wrapped_data_buf = NULL; + if (pasn->update_extra_ies && pasn->cb_ctx) + pasn->update_extra_ies(pasn->cb_ctx, pasn->peer_addr, false); + + wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len); + /* Add the MIC */ mic_len = pasn_mic_len(pasn->akmp, pasn->cipher); wpabuf_put_u8(buf, WLAN_EID_MIC); @@ -747,13 +801,25 @@ void wpa_pasn_reset(struct pasn_data *pasn) pasn->derive_kdk = false; pasn->rsn_ie = NULL; pasn->rsn_ie_len = 0; - pasn->rsnxe_ie = NULL; pasn->custom_pmkid_valid = false; + if (pasn->rsnxe_ie) { + os_free((u8 *)pasn->rsnxe_ie); + pasn->rsnxe_ie = NULL; + pasn->rsnxe_ie_len = 0; + } if (pasn->extra_ies) { os_free((u8 *) pasn->extra_ies); pasn->extra_ies = NULL; } + if (pasn->action_frame_wrapper) { + wpabuf_free(pasn->action_frame_wrapper); + pasn->action_frame_wrapper = NULL; + } + if (pasn->frame) { + wpabuf_free(pasn->frame); + pasn->frame = NULL; + } } @@ -928,12 +994,12 @@ static int wpas_pasn_send_auth_1(struct pasn_data *pasn, const u8 *own_addr, wpabuf_head(frame), wpabuf_len(frame), 0, pasn->freq, 1000); - wpabuf_free(frame); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame"); + wpabuf_free(frame); goto fail; } - + pasn->frame = frame; return 0; fail: @@ -1233,7 +1299,7 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, pasn->own_addr, pasn->peer_addr, wpabuf_head(secret), wpabuf_len(secret), &pasn->ptk, pasn->akmp, pasn->cipher, - pasn->kdk_len); + pasn->kdk_len, pasn->kek_len); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK"); goto fail; @@ -1323,7 +1389,10 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame"); - frame = wpas_pasn_build_auth_3(pasn); + if (pasn->parse_encrypted_data && pasn->cb_ctx) + pasn->parse_encrypted_data(pasn->cb_ctx, data, len); + + frame = wpas_pasn_build_auth_3(pasn, data, len); if (!frame) { wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame"); goto fail; @@ -1332,12 +1401,13 @@ int wpa_pasn_auth_rx(struct pasn_data *pasn, const u8 *data, size_t len, ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head(frame), wpabuf_len(frame), 0, pasn->freq, 100); - wpabuf_free(frame); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame"); + wpabuf_free(frame); goto fail; } + pasn->frame = frame; wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK"); pasn->status = WLAN_STATUS_SUCCESS; diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c index b99136492..89f5fff56 100644 --- a/src/pasn/pasn_responder.c +++ b/src/pasn/pasn_responder.c @@ -26,6 +26,50 @@ #include "pasn_common.h" +struct rsn_pmksa_cache * pasn_responder_pmksa_cache_init(void) +{ + return pmksa_cache_auth_init(NULL, NULL); +} + + +void pasn_responder_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +{ + return pmksa_cache_auth_deinit(pmksa); +} + + +int pasn_responder_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, u8 *own_addr, + u8 *bssid, u8 *pmk, size_t pmk_len) +{ + if (pmksa_cache_auth_add(pmksa, pmk, pmk_len, NULL, NULL, 0, own_addr, + bssid, 0, NULL, WPA_KEY_MGMT_SAE)) + return 0; + return -1; +} + + +int pasn_responder_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, u8 *bssid, + u8 *pmkid, u8 *pmk, size_t *pmk_len) +{ + struct rsn_pmksa_cache_entry *entry; + + entry = pmksa_cache_auth_get(pmksa, bssid, NULL); + if (entry) { + os_memcpy(pmkid, entry->pmkid, PMKID_LEN); + os_memcpy(pmk, entry->pmk, entry->pmk_len); + *pmk_len = entry->pmk_len; + return 0; + } + return -1; +} + + +void pasn_responder_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa) +{ + return pmksa_cache_auth_flush(pmksa); +} + + void pasn_set_responder_pmksa(struct pasn_data *pasn, struct rsn_pmksa_cache *pmksa) { @@ -349,7 +393,7 @@ pasn_derive_keys(struct pasn_data *pasn, ret = pasn_pmk_to_ptk(pmk, pmk_len, peer_addr, own_addr, wpabuf_head(secret), wpabuf_len(secret), &pasn->ptk, pasn->akmp, - pasn->cipher, pasn->kdk_len); + pasn->cipher, pasn->kdk_len, pasn->kek_len); if (ret) { wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK"); return -1; @@ -414,7 +458,7 @@ static void handle_auth_pasn_comeback(struct pasn_data *pasn, "PASN: comeback: STA=" MACSTR, MAC2STR(peer_addr)); ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf), - wpabuf_len(buf), 0, 0, 0); + wpabuf_len(buf), 0, pasn->freq, 0); if (ret) wpa_printf(MSG_INFO, "PASN: Failed to send comeback frame 2"); @@ -502,6 +546,10 @@ int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr, if (rsnxe_ie) wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]); + if (pasn->update_extra_ies && pasn->cb_ctx) + pasn->update_extra_ies(pasn->cb_ctx, peer_addr, + pmkid ? true : false); + wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len); /* Add the mic */ @@ -579,12 +627,14 @@ done: MAC2STR(peer_addr)); ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf), - wpabuf_len(buf), 0, 0, 0); - if (ret) + wpabuf_len(buf), 0, pasn->freq, 0); + if (ret) { wpa_printf(MSG_INFO, "send_auth_reply: Send failed"); + goto fail; + } wpabuf_free(rsn_buf); - wpabuf_free(buf); + pasn->frame = buf; return ret; fail: wpabuf_free(wrapped_data_buf); @@ -1020,6 +1070,9 @@ int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr, wpabuf_free(wrapped_data); } + if (pasn->parse_encrypted_data && pasn->cb_ctx) + pasn->parse_encrypted_data(pasn->cb_ctx, (const u8 *) mgmt, len); + wpa_printf(MSG_INFO, "PASN: Success handling transaction == 3. Store PTK"); return 0; diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 4ab9f0b88..c3465a1d0 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -5952,6 +5952,35 @@ static void wpas_link_reconfig(struct wpa_supplicant *wpa_s) } +int wpas_pasn_auth(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len, int freq) +{ + int ret = 0; + struct ieee802_11_elems elems; + + if (len < 24) { + wpa_printf(MSG_DEBUG, "nl80211: Too short management frame"); + return -2; + } + + if (ieee802_11_parse_elems(mgmt->u.auth.variable, + len - offsetof(struct ieee80211_mgmt, + u.auth.variable), + &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "PASN: Failed parsing Authentication frame"); + return -2; + } + + if (!elems.p2p2_ie || !elems.p2p2_ie_len) + ret = wpas_pasn_auth_rx(wpa_s, mgmt, len); + else + ret = wpas_p2p_pasn_auth_rx(wpa_s, mgmt, len, freq); + + return ret; +} + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { @@ -6182,11 +6211,22 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #endif /* CONFIG_WNM */ #ifdef CONFIG_PASN if (data->tx_status.type == WLAN_FC_TYPE_MGMT && - data->tx_status.stype == WLAN_FC_STYPE_AUTH && - wpas_pasn_auth_tx_status(wpa_s, data->tx_status.data, - data->tx_status.data_len, - data->tx_status.ack) == 0) - break; + data->tx_status.stype == WLAN_FC_STYPE_AUTH) { + if (!wpa_s->pasn_auth_work && + wpa_s->p2p_pasn_auth_work) { + if (wpas_p2p_pasn_auth_tx_status(wpa_s, + data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack) == 0) + break; + } else { + if (wpas_pasn_auth_tx_status(wpa_s, + data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack) == 0) + break; + } + } #endif /* CONFIG_PASN */ #ifdef CONFIG_AP if (wpa_s->ap_iface == NULL) { @@ -6458,8 +6498,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } #ifdef CONFIG_PASN if (stype == WLAN_FC_STYPE_AUTH && - wpas_pasn_auth_rx(wpa_s, mgmt, - data->rx_mgmt.frame_len) != -2) + wpas_pasn_auth(wpa_s, mgmt, data->rx_mgmt.frame_len, + data->rx_mgmt.freq) != -2) break; #endif /* CONFIG_PASN */ diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index d2e661318..5e04add1c 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -38,7 +38,6 @@ #include "p2p_supplicant.h" #include "wifi_display.h" - /* * How many times to try to scan to find the GO before giving up on join * request. @@ -1716,6 +1715,28 @@ static void wpas_send_action_done(void *ctx) offchannel_send_action_done(wpa_s); } +struct wpa_p2p_pasn_auth_work { + u8 peer_addr[ETH_ALEN]; + bool verify; + int freq; +}; + + +static void wpas_p2p_pasn_free_auth_work(struct wpa_p2p_pasn_auth_work *awork) +{ + os_free(awork); +} + + +static void wpas_p2p_pasn_cancel_auth_work(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "P2P PASN: Cancel p2p-pasn-start-auth work"); + + /* Remove pending/started work */ + radio_remove_works(wpa_s, "p2p-pasn-start-auth", 0); +} + + static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params) @@ -2391,6 +2412,12 @@ static void wpas_p2p_group_formation_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; + + if (wpa_s->p2p_pasn_auth_work) { + wpas_p2p_pasn_cancel_auth_work(wpa_s); + wpa_s->p2p_pasn_auth_work = NULL; + } + wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out"); wpas_p2p_group_formation_failed(wpa_s, 0); } @@ -2456,6 +2483,11 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) wpa_s->roc_waiting_drv_freq = 0; } + if (wpa_s->p2p_pasn_auth_work) { + wpas_p2p_pasn_cancel_auth_work(wpa_s); + wpa_s->p2p_pasn_auth_work = NULL; + } + if (res->status) { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_FAILURE "status=%d", @@ -4822,6 +4854,68 @@ static int wpas_p2p_get_pref_freq_list(void *ctx, int go, WPA_IF_P2P_CLIENT, len, freq_list); } + +static void wpas_p2p_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpa_p2p_pasn_auth_work *awork = work->ctx; + struct p2p_data *p2p = wpa_s->global->p2p; + const u8 *peer_addr = NULL; + + if (deinit) { + if (!work->started) { + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->p2pdev, NULL); + } + os_free(awork); + return; + } + + if (!is_zero_ether_addr(awork->peer_addr)) + peer_addr = awork->peer_addr; + if (p2p_initiate_pasn_auth(p2p, peer_addr, awork->freq)) { + wpa_printf(MSG_DEBUG, + "P2P PASN: Failed to start PASN authentication"); + goto fail; + } + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->p2pdev, NULL); + eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, + wpas_p2p_group_formation_timeout, + wpa_s->p2pdev, NULL); + wpa_s->p2p_pasn_auth_work = work; + return; +fail: + wpas_p2p_pasn_free_auth_work(awork); + work->ctx = NULL; + radio_work_done(work); +} + +static int wpas_p2p_initiate_pasn_auth(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int freq) +{ + struct wpa_p2p_pasn_auth_work *awork; + + wpas_p2p_pasn_cancel_auth_work(wpa_s); + wpa_s->p2p_pasn_auth_work = NULL; + + awork = os_zalloc(sizeof(*awork)); + if (!awork) + return -1; + + awork->freq = freq; + os_memcpy(awork->peer_addr, peer_addr, ETH_ALEN); + + if (radio_add_work(wpa_s, freq, "p2p-pasn-start-auth", 1, + wpas_p2p_pasn_auth_start_cb, awork) < 0) { + wpas_p2p_pasn_free_auth_work(awork); + return -1; + } + + wpa_printf(MSG_DEBUG, "P2P PASN: Auth work successfully added"); + return 0; +} + static void wpas_p2p_send_bootstrap_comeback(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; @@ -4886,8 +4980,46 @@ static void wpas_bootstrap_completed(void *ctx, const u8 *addr, int status, if (status) return; + + wpas_p2p_initiate_pasn_auth(wpa_s, addr, freq); } +static int wpas_p2p_pasn_send_mlme(void *ctx, const u8 *data, size_t data_len, + int noack, unsigned int freq, + unsigned int wait) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpa_drv_send_mlme(wpa_s, data, data_len, noack, freq, wait); +} + + +static int wpas_p2p_pasn_update_extra_ies(void *ctx, const u8 *peer_addr, + bool dira) +{ + struct wpa_supplicant *wpa_s = ctx; + struct p2p_data *p2p = wpa_s->global->p2p; + + return p2p_pasn_update_extra_ies(p2p, peer_addr, dira); +} + + +static int wpas_p2p_pasn_parse_encrypted_data(void *ctx, const u8 *data, + size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + struct p2p_data *p2p = wpa_s->global->p2p; + + return p2p_pasn_parse_encrypted_data(p2p, data, len); +} + +int wpas_p2p_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data, + size_t data_len, u8 acked) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + + return p2p_pasn_auth_tx_status(p2p, data, data_len, acked); +} int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s) { @@ -5011,6 +5143,9 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) p2p.register_bootstrap_comeback = wpas_p2p_register_bootstrap_comeback; p2p.bootstrap_req_rx = wpas_bootstrap_req_rx; p2p.bootstrap_completed = wpas_bootstrap_completed; + p2p.pasn_send_mgmt = wpas_p2p_pasn_send_mlme; + p2p.pasn_update_extra_ies = wpas_p2p_pasn_update_extra_ies; + p2p.pasn_parse_encrypted_data = wpas_p2p_pasn_parse_encrypted_data; os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN); os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN); @@ -10349,3 +10484,14 @@ void wpas_p2p_process_usd_elems(struct wpa_supplicant *wpa_s, const u8 *buf, return; p2p_process_usd_elems(p2p, buf, buf_len, peer_addr, freq); } + +int wpas_p2p_pasn_auth_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len, + int freq) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + + if (wpa_s->global->p2p_disabled || !p2p) + return -2; + return p2p_pasn_auth_rx(p2p, mgmt, len, freq); +} diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index a2cb78d7c..4f798980d 100644 --- a/wpa_supplicant/p2p_supplicant.h +++ b/wpa_supplicant/p2p_supplicant.h @@ -227,7 +227,9 @@ int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq, int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s); int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s); struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s); - +int wpas_p2p_pasn_auth_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len, + int freq); #else /* CONFIG_P2P */ static inline int @@ -357,6 +359,12 @@ static inline struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s) { return NULL; } +static int wpas_p2p_pasn_auth_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len, + int freq) +{ + return 0; +} #endif /* CONFIG_P2P */ diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c index 1bb38f73d..5959fca2d 100644 --- a/wpa_supplicant/pasn_supplicant.c +++ b/wpa_supplicant/pasn_supplicant.c @@ -806,6 +806,11 @@ int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s, if (!wpa_s->pasn_auth_work) return -2; + if (pasn->frame) { + wpabuf_free(pasn->frame); + pasn->frame = NULL; + } + pasn_register_callbacks(pasn, wpa_s, wpas_pasn_send_mlme, NULL); ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data); if (ret == 0) { diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 2724183da..0740bdab4 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1588,6 +1588,9 @@ struct wpa_supplicant { struct wpa_radio_work *pasn_auth_work; unsigned int pasn_count; struct pasn_auth *pasn_params; +#ifdef CONFIG_P2P + struct wpa_radio_work *p2p_pasn_auth_work; +#endif /* CONFIG_P2P */ #endif /* CONFIG_PASN */ bool is_6ghz_enabled; @@ -2010,5 +2013,7 @@ bool wpas_ap_link_address(struct wpa_supplicant *wpa_s, const u8 *addr); void wpas_p2p_process_usd_elems(struct wpa_supplicant *wpa_s, const u8 *buf, u16 buf_len, const u8 *peer_addr, unsigned int freq); +int wpas_p2p_pasn_auth_tx_status(struct wpa_supplicant *wpa_s, const u8 *data, + size_t data_len, u8 acked); #endif /* WPA_SUPPLICANT_I_H */ -- 2.34.1 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap