Add P2P2 support for GO negotiation wrapped in PASN authentication frames as a action wrapper attribute. Signed-off-by: Shivani Baranwal <quic_shivbara@xxxxxxxxxxx> --- src/ap/ieee802_11.c | 16 +- src/common/ieee802_11_common.c | 4 + src/common/ieee802_11_common.h | 2 + src/common/ieee802_11_defs.h | 2 + src/p2p/p2p.c | 808 +++++++++++++++++++++++++++++++++++++- src/p2p/p2p.h | 85 ++++ src/p2p/p2p_go_neg.c | 4 +- src/p2p/p2p_i.h | 27 ++ src/p2p/p2p_parse.c | 20 + src/p2p/p2p_pd.c | 6 + src/pasn/pasn_common.h | 7 + src/pasn/pasn_initiator.c | 50 ++- src/pasn/pasn_responder.c | 20 +- wpa_supplicant/events.c | 56 ++- wpa_supplicant/p2p_supplicant.c | 161 +++++++- wpa_supplicant/p2p_supplicant.h | 11 +- wpa_supplicant/pasn_supplicant.c | 5 + wpa_supplicant/wpa_supplicant_i.h | 5 + 18 files changed, 1260 insertions(+), 29 deletions(-) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 38fcba5..3c5a8dd 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -2487,6 +2487,10 @@ static void pasn_fils_auth_resp(struct hostapd_data *hapd, fils->erp_resp = erp_resp; ret = handle_auth_pasn_resp(sta->pasn, hapd->own_addr, sta->addr, NULL, WLAN_STATUS_SUCCESS); + if (sta->pasn->frame) { + wpabuf_free(sta->pasn->frame); + sta->pasn->frame = NULL; + } fils->erp_resp = NULL; if (ret) { @@ -2800,6 +2804,8 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *mgmt, size_t len, u16 trans_seq, u16 status) { + int ret; + if (hapd->conf->wpa != WPA_PROTO_RSN) { wpa_printf(MSG_INFO, "PASN: RSN is not configured"); return; @@ -2831,9 +2837,15 @@ static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta, hapd_initialize_pasn(hapd, sta); hapd_pasn_update_params(hapd, sta, mgmt, len); - if (handle_auth_pasn_1(sta->pasn, hapd->own_addr, - sta->addr, mgmt, len) < 0) + ret = handle_auth_pasn_1(sta->pasn, hapd->own_addr, sta->addr, + mgmt, len); + if (sta->pasn->frame) { + wpabuf_free(sta->pasn->frame); + sta->pasn->frame = NULL; + } + if (ret < 0) ap_free_sta(hapd, sta); + } else if (trans_seq == 3) { if (!sta->pasn) { wpa_printf(MSG_DEBUG, diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index 2d4540b..b16564d 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -413,6 +413,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 e4321b5..7ca99f3 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; @@ -141,6 +142,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 0e88797..db1033a 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/p2p/p2p.c b/src/p2p/p2p.c index 01490e2..6e2a97c 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -15,10 +15,14 @@ #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "crypto/sha256.h" +#include "crypto/sha384.h" #include "crypto/crypto.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); @@ -242,6 +246,11 @@ void p2p_go_neg_failed(struct p2p_data *p2p, int status) peer->go_neg_conf = NULL; p2p->go_neg_peer = NULL; +#ifdef CONFIG_PASN + if (peer->p2p2 && peer->pasn) + wpa_pasn_reset(peer->pasn); +#endif /* CONFIG_PASN */ + os_memset(&res, 0, sizeof(res)); res.status = status; os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN); @@ -959,6 +968,14 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev) dev->bootstrap_params = NULL; } +#ifdef CONFIG_PASN + if (dev->pasn) { + wpa_pasn_reset(dev->pasn); + pasn_data_deinit(dev->pasn); + dev->pasn = NULL; + } +#endif /* CONFIG_PASN */ + wpabuf_free(dev->info.wfd_subelems); wpabuf_free(dev->info.vendor_elems); wpabuf_free(dev->go_neg_conf); @@ -1861,8 +1878,13 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) os_memset(&res, 0, sizeof(res)); res.role_go = go; + + if (is_zero_ether_addr(peer->interface_addr)) + os_memcpy(peer->interface_addr, peer->intended_addr, ETH_ALEN); + os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN); - os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN); + os_memcpy(res.peer_interface_addr, peer->interface_addr, ETH_ALEN); + res.wps_method = peer->wps_method; if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) @@ -1911,6 +1933,11 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) wpabuf_free(peer->go_neg_conf); peer->go_neg_conf = NULL; +#ifdef CONFIG_PASN + if (peer->p2p2 && peer->pasn) + wpa_pasn_reset(peer->pasn); +#endif /* CONFIG_PASN */ + p2p_set_state(p2p, P2P_PROVISIONING); p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); } @@ -3012,7 +3039,18 @@ int p2p_pairing_info_init(struct p2p_data *p2p) os_memcpy(pairing_info->dev_ik.dik_data, p2p->cfg->pairing_config.dik_data, p2p->cfg->pairing_config.dik_len); + + if (!p2p->cfg->dev_password_len) { + p2p->cfg->dev_password_len = 10; + p2p_random(p2p->cfg->dev_password, p2p->cfg->dev_password_len); + p2p->cfg->dev_password[p2p->cfg->dev_password_len] = '\0'; + } + p2p->pairing_info = pairing_info; +#ifdef CONFIG_PASN + p2p->initiator_pmksa = pasn_initiator_pmksa_cache_init(); + p2p->responder_pmksa = pasn_responder_pmksa_cache_init(); +#endif /* CONFIG_PASN */ return 0; } @@ -3086,6 +3124,10 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) void p2p_pairing_info_deinit(struct p2p_data *p2p) { +#ifdef CONFIG_PASN + pasn_initiator_pmksa_cache_deinit(p2p->initiator_pmksa); + pasn_responder_pmksa_cache_deinit(p2p->responder_pmksa); +#endif /* CONFIG_PASN */ os_free(p2p->pairing_info); } @@ -4975,8 +5017,11 @@ int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, u8 *iface_addr) { struct p2p_device *dev = p2p_get_device(p2p, dev_addr); - if (dev == NULL || is_zero_ether_addr(dev->interface_addr)) + if (dev == NULL || is_zero_ether_addr(dev->interface_addr)) { + wpa_printf(MSG_DEBUG, "P2P: Failed to get interface addr from dev addr " + MACSTR, MAC2STR(dev_addr)); return -1; + } os_memcpy(iface_addr, dev->interface_addr, ETH_ALEN); return 0; } @@ -4986,8 +5031,11 @@ int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr, u8 *dev_addr) { struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr); - if (dev == NULL) + if (!dev) { + wpa_printf(MSG_DEBUG, "P2P: Failed to get device addr from iface addr " + MACSTR, MAC2STR(iface_addr)); return -1; + } os_memcpy(dev_addr, dev->info.p2p_device_addr, ETH_ALEN); return 0; } @@ -5862,3 +5910,757 @@ void p2p_process_usd_elems(struct p2p_data *p2p, const u8 *ies, u16 ies_len, p2p_parse_free(&msg); } + +#ifdef CONFIG_PASN +int p2p_prepare_pasn_extra_ie(struct p2p_data *p2p, struct wpabuf *extra_ies, + struct wpabuf *frame) +{ + 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); + + 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(pasn->pasn_groups, 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, bool verify) +{ + 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; + + if ((p2p->cfg->pairing_config.pasn_type & 0xc) && + (dev->info.pairing_config.pasn_type & 0xc)) { + pasn->group = 20; + pasn->cipher = WPA_CIPHER_GCMP_256; + pasn->kek_len = WPA_KEK_256; + pasn->pasn_groups = p2p->cfg->pairing_config.pasn_groups; + } else { + 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 if (verify) { + pasn->akmp = WPA_KEY_MGMT_SAE; + } 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, false); + pasn = dev->pasn; + + pasn_initiator_pmksa_cache_remove(pasn->pmksa, (u8 *)addr); + + /* 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)) { + 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.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 (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->role = P2P_ROLE_PAIRING_RESPONDER; + p2p_pasn_initialize(p2p, dev, mgmt->sa, freq, true); + 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 (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; + /* FIXME: If go neg resp is with failure status, + how go_neg_failed is indicated to host */ + } 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 && + p2p->cfg->dev_password_len) { + wpabuf_put_u8(p2p2_ie, P2P_ATTR_PASSWORD); + wpabuf_put_le16(p2p2_ie, p2p->cfg->dev_password_len); + wpabuf_put_data(p2p2_ie, p2p->cfg->dev_password, + p2p->cfg->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) +{ + 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)) { + 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.dik_data, pos, 16); + dev->info.dik_len = 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.dik_data, 0, + sizeof(dev->info.dik_data)); + memset(dev->info.password, 0, + sizeof(dev->info.password)); + dev->info.password_len = 0; + return -1; + } + forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk)); + } + 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; +} +#endif diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index c9bc12f..1b1c19f 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -372,6 +372,11 @@ struct p2p_pairing_config { /* length of DevIK */ size_t dik_len; + + /** + * The set of supported PASN groups + */ + int pasn_groups[4]; }; @@ -479,6 +484,36 @@ 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 dik_data[DEVICE_IDENTITY_KEY_MAX_LEN]; + + /** + * Device Identity key length + */ + u16 dik_len; + + /** + * 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 { @@ -658,6 +693,16 @@ struct p2p_config { unsigned int passphrase_len; /** + * password used during group formation post opportunistic pasn auth + */ + char dev_password[100]; + + /** + * password length. Non zero if valid + */ + u16 dev_password_len; + + /** * p2p_pairing_config - P2P Pairing configuration */ struct p2p_pairing_config pairing_config; @@ -1269,6 +1314,38 @@ 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); + + /** + * pasn_update_extra_ies - Function handler to update protocol specific + * IEs in pasn auth frames + * @ctx: Callback context from cb_ctx + * @peer_addr : peer mac address + * Returns: 0 on success, -1 on failure + */ + int (*pasn_update_extra_ies)(void *ctx, const u8 *peer_addr); + + /** + * pasn_parse_encrypted_data - Function handler to parse encrypted data + * with KEK received in pasn auth frames + * @ctx: Callback context from cb_ctx + * @data : data to be decrypted + * @len: length of encrypted data + * Returns: 0 on success, -1 on failure + */ + int (*pasn_parse_encrypted_data)(void *ctx, const u8 *data, size_t len); }; @@ -2571,4 +2648,12 @@ 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); +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_go_neg.c b/src/p2p/p2p_go_neg.c index 5798018..1f6923d 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -135,8 +135,8 @@ 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) { u8 group_capab; size_t extra = 0; diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index d7a5dc1..e0d2ee0 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -37,6 +37,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 */ @@ -188,6 +195,12 @@ 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; }; struct p2p_sd_query { @@ -630,6 +643,10 @@ 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 */ @@ -761,6 +778,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; }; @@ -919,6 +942,8 @@ 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); @@ -1021,6 +1046,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, bool verify); void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) PRINTF_FORMAT(2, 3); diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c index a70e180..de2a43f 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 56022ff..f1856d2 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -824,6 +824,12 @@ 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; +#ifdef CONFIG_PASN + p2p_pasn_initialize(p2p, dev, sa, rx_freq, false); +#endif /* CONFIG_PASN */ + } out: /* * Send PD Bootstrapping Response for the PD Request diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h index e3ff746..8eb3bce 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]; @@ -130,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 @@ -151,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); + + int (*parse_encrypted_data)(void *ctx, const u8 *data, size_t len); }; /* Initiator */ diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c index dbcc91a..9d97895 100644 --- a/src/pasn/pasn_initiator.c +++ b/src/pasn/pasn_initiator.c @@ -646,7 +646,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); @@ -675,11 +678,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; @@ -713,6 +718,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); + + 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); @@ -806,13 +816,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; + } } @@ -982,17 +1004,21 @@ static int wpas_pasn_send_auth_1(struct pasn_data *pasn, const u8 *own_addr, wpa_printf(MSG_DEBUG, "PASN: Failed building 1st auth frame"); goto fail; } + if (pasn->frame) { + wpabuf_free(pasn->frame); + pasn->frame = NULL; + } ret = pasn->send_mgmt(pasn->cb_ctx, 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: @@ -1382,21 +1408,29 @@ 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; } + if (pasn->frame) { + wpabuf_free(pasn->frame); + pasn->frame = NULL; + } 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 c75ba87..e5216a0 100644 --- a/src/pasn/pasn_responder.c +++ b/src/pasn/pasn_responder.c @@ -473,7 +473,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"); @@ -561,6 +561,9 @@ 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); + wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len); /* Add the mic */ @@ -636,14 +639,20 @@ done: wpa_printf(MSG_DEBUG, "PASN: Building frame 2: success; resp STA=" MACSTR, MAC2STR(peer_addr)); + if (pasn->frame) { + wpabuf_free(pasn->frame); + pasn->frame = NULL; + } 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); @@ -1079,6 +1088,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 46e7cf1..1c7992e 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -6093,6 +6093,37 @@ static void wpas_link_reconfig(struct wpa_supplicant *wpa_s) } +#ifdef CONFIG_PASN +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; +} +#endif /* CONFIG_PASN */ + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { @@ -6323,11 +6354,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) { @@ -6599,8 +6641,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 6e7cf8f..0cffb99 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -40,6 +40,7 @@ #include "crypto/random.h" + /* * How many times to try to scan to find the GO before giving up on join * request. @@ -165,7 +166,10 @@ wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s, static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx); static int wpas_p2p_disallowed_freq(struct wpa_global *global, unsigned int freq); - +#ifdef CONFIG_PASN +static int wpas_p2p_initiate_pasn_auth(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int freq); +#endif /* CONFIG_PASN */ static int wpas_get_6ghz_he_chwidth_capab(struct hostapd_hw_modes *mode) { @@ -1717,6 +1721,29 @@ static void wpas_send_action_done(void *ctx) offchannel_send_action_done(wpa_s); } +#ifdef CONFIG_PASN +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); +} +#endif /* CONFIG_PASN */ + static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params) @@ -2392,6 +2419,12 @@ static void wpas_p2p_group_formation_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; +#ifdef CONFIG_PASN + if (wpa_s->p2p_pasn_auth_work) { + wpas_p2p_pasn_cancel_auth_work(wpa_s); + wpa_s->p2p_pasn_auth_work = NULL; + } +#endif /* CONFIG_PASN */ wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out"); wpas_p2p_group_formation_failed(wpa_s, 0); } @@ -2456,6 +2489,12 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) wpa_s->off_channel_freq = 0; wpa_s->roc_waiting_drv_freq = 0; } +#ifdef CONFIG_PASN + if (wpa_s->p2p_pasn_auth_work) { + wpas_p2p_pasn_cancel_auth_work(wpa_s); + wpa_s->p2p_pasn_auth_work = NULL; + } +#endif /* CONFIG_PASN */ if (res->status) { wpa_msg_global(wpa_s, MSG_INFO, @@ -4888,8 +4927,109 @@ static void wpas_bootstrap_completed(void *ctx, const u8 *addr, int status, if (status) return; +#ifdef CONFIG_PASN + wpas_p2p_initiate_pasn_auth(wpa_s, addr, freq); +#endif /* CONFIG_PASN */ +} + +#ifdef CONFIG_PASN +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 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) +{ + struct wpa_supplicant *wpa_s = ctx; + struct p2p_data *p2p = wpa_s->global->p2p; + + return p2p_pasn_update_extra_ies(p2p, peer_addr); +} + + +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); +} +#endif int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s) { @@ -5013,7 +5153,11 @@ 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; - +#ifdef CONFIG_PASN + 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; +#endif /* CONFIG_PASN */ 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); p2p.dev_name = wpa_s->conf->device_name; @@ -10383,3 +10527,16 @@ 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); } + +#ifdef CONFIG_PASN +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); +} +#endif /* CONFIG_PASN */ diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index a2cb78d..5612d83 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 @@ -358,6 +360,13 @@ static inline struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s) return NULL; } +static inline 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 */ #endif /* P2P_SUPPLICANT_H */ diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c index 89edad4..f4a3bb2 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 35f541f..51da6ff 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1593,6 +1593,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; @@ -2028,5 +2031,7 @@ int wpas_get_owe_trans_network(const u8 *owe_ie, const u8 **bssid, 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.7.4 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap