From patchwork Mon Jul 8 09:32:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shivani Baranwal X-Patchwork-Id: 1958397 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:3::133; helo=bombadil.infradead.org; envelope-from=hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=patchwork.ozlabs.org) Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:3::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WJL6m59W3z1xr9 for ; Tue, 9 Jul 2024 22:35:10 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=+/PPIs06h08o9u9nwFDZbIox4gvQ+PsIGM/FC70vPd4=; b=RUtCHf4SUVfti8 +Ph1QfyFjvRC6OMXFy0m70zhdJzrQ8xessJm5OhJZj9P3PJi072YoieHgX0ufmPqVsPJKB6U1uhqd 8JQKuMvHCiHtge53GIWNG8fHQ2qU6IzxnRf1daY5rWAWJctLA5GTNOFP0aMBLTpO0cfwUBFNe9zP5 MgU5jNKUywyPtdVPldbL8u4vUK6Q8L3IxXO278gGYubQ9QCWkiGrIAQ3T2hpXFFt1c9DAIaYmn3Wj SN/2IyYqGYPCE0zRdF0iU0smc3DlaI1i2LQKCvT3BzTvV7LEgByDIs2dCITkzZ5xILIImkDP5XIeJ g6SouEMTPFV4RiRfaZ3Q==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1sRA36-000000079R7-3FUs; Tue, 09 Jul 2024 12:34:24 +0000 Received: from mx0a-0031df01.pphosted.com ([205.220.168.131]) by bombadil.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1sQkoY-00000003IVv-3UI1 for hostap@lists.infradead.org; Mon, 08 Jul 2024 09:37:49 +0000 Received: from pps.filterd (m0279864.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 4680TpI9019778 for ; Mon, 8 Jul 2024 09:37:42 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= cc:content-type:date:from:in-reply-to:message-id:mime-version :references:subject:to; s=qcppdkim1; bh=4QTiV/d4hVpVqeagGHph7Svo oELBjfH2M3q//9rmLkg=; b=Qk4rOWcCms11FMORQN+maEMCejCs6D/vHtn/mal0 WEvJ+7mrMN6yUSr365riDfIuqd17iOpqnXAWuyy9gTxtQ3p+RmzZeYeBTfJLthwy /SHh6Ml3gJ9xxgNRCwFGd+weFb1GSzhR4PGKPUBtnZHGPsYimmkIWSjNIgVL4Vtp UWOgu5DK0orY/exNsWsU3uChG8wjvBaBdiFluylEjRpbuefVM6alpMChn/5a2yl8 bK70Hw8tFwqOnxpegjYhbn72oJL1EYF3ICnFm42MLFDRQZMtFzzkcKtR8QhuI8jM Hz0K+mWjSQ05Jma6pJ4p2moUQX4LjI9PEi60se+bmBy9BQ== Received: from nalasppmta03.qualcomm.com (Global_NAT1.qualcomm.com [129.46.96.20]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 406y3hb2sw-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for ; Mon, 08 Jul 2024 09:37:41 +0000 (GMT) Received: from nalasex01a.na.qualcomm.com (nalasex01a.na.qualcomm.com [10.47.209.196]) by NALASPPMTA03.qualcomm.com (8.17.1.19/8.17.1.19) with ESMTPS id 4689beP7018949 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for ; Mon, 8 Jul 2024 09:37:41 GMT Received: from hu-shivbara-hyd.qualcomm.com (10.80.80.8) by nalasex01a.na.qualcomm.com (10.47.209.196) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Mon, 8 Jul 2024 02:37:39 -0700 From: Shivani Baranwal To: CC: Subject: [PATCH 09/13] P2P: Add support for GO negotiation wrapped in PASN auth frame Date: Mon, 8 Jul 2024 15:02:25 +0530 Message-ID: <1720431149-7494-9-git-send-email-quic_shivbara@quicinc.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1720431149-7494-1-git-send-email-quic_shivbara@quicinc.com> References: <1720431149-7494-1-git-send-email-quic_shivbara@quicinc.com> MIME-Version: 1.0 X-Originating-IP: [10.80.80.8] X-ClientProxiedBy: nasanex01b.na.qualcomm.com (10.46.141.250) To nalasex01a.na.qualcomm.com (10.47.209.196) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-ORIG-GUID: UQshQYBxBusQG13R71ifrKsh4BWtMMVB X-Proofpoint-GUID: UQshQYBxBusQG13R71ifrKsh4BWtMMVB X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1039,Hydra:6.0.680,FMLib:17.12.28.16 definitions=2024-07-08_04,2024-07-05_01,2024-05-17_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 malwarescore=0 bulkscore=0 spamscore=0 adultscore=0 mlxscore=0 lowpriorityscore=0 suspectscore=0 clxscore=1015 mlxlogscore=999 impostorscore=0 phishscore=0 priorityscore=1501 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2406140001 definitions=main-2407080075 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240708_023743_214557_5D1F7C73 X-CRM114-Status: GOOD ( 21.85 ) X-Spam-Score: -0.2 (/) X-Spam-Report: Spam detection software, running on the system "bombadil.infradead.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: Add P2P2 support for GO negotiation wrapped in PASN authentication frames as a action wrapper attribute. Signed-off-by: Shivani Baranwal 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(struc [...] Content analysis details: (-0.2 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature X-Mailman-Approved-At: Tue, 09 Jul 2024 05:34:22 -0700 X-BeenThere: hostap@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "Hostap" Errors-To: hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Add P2P2 support for GO negotiation wrapped in PASN authentication frames as a action wrapper attribute. Signed-off-by: Shivani Baranwal 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 */