Wireless Daemon for Linux
 help / color / mirror / Atom feed
From: Andrew Zaborowski <andrew.zaborowski@intel.com>
To: iwd@lists.01.org
Subject: [PATCH 7/8] ap: Parse WSC PBC association request and build response
Date: Fri, 28 Aug 2020 14:46:48 +0200	[thread overview]
Message-ID: <20200828124649.78677-7-andrew.zaborowski@intel.com> (raw)
In-Reply-To: <20200828124649.78677-1-andrew.zaborowski@intel.com>

[-- Attachment #1: Type: text/plain, Size: 10999 bytes --]

Check the conditions for PBC enrollee registration when we receive the
Association Request with WSC IE and indicate to the enrollee whether we
accept the association using a WSC IE in the Association Response.
After this, a NULL sta->assoc_rsne indicates that the station is not
establishing the RSNA and is a WSC enrollee.
---
 src/ap.c | 186 ++++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 151 insertions(+), 35 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index df0a9942..d7d0bbad 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -91,6 +91,8 @@ struct sta_state {
 	struct eapol_sm *sm;
 	struct handshake_state *hs;
 	uint32_t gtk_query_cmd_id;
+	uint8_t wsc_uuid_e[16];
+	bool wsc_v2;
 };
 
 struct ap_wsc_pbc_probe_record {
@@ -906,7 +908,7 @@ static void ap_fail_assoc_resp_cb(struct l_genl_msg *msg, void *user_data)
 }
 
 static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
-				const uint8_t *dest, uint16_t aid,
+				const uint8_t *dest,
 				enum mmpdu_reason_code status_code,
 				bool reassoc, l_genl_msg_func_t callback)
 {
@@ -934,7 +936,7 @@ static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
 	resp = (void *) mmpdu_body(mpdu);
 	l_put_le16(capability, &resp->capability);
 	resp->status_code = L_CPU_TO_LE16(status_code);
-	resp->aid = L_CPU_TO_LE16(aid | 0xc000);
+	resp->aid = sta ? L_CPU_TO_LE16(sta->aid | 0xc000) : 0;
 
 	/* Supported Rates IE */
 	resp->ies[ies_len++] = IE_TYPE_SUPPORTED_RATES;
@@ -956,6 +958,38 @@ static uint32_t ap_assoc_resp(struct ap_state *ap, struct sta_state *sta,
 	resp->ies[ies_len++] = count;
 	ies_len += count;
 
+	if (sta && !sta->assoc_rsne) {
+		struct wsc_association_response wsc_resp = {};
+		uint8_t *wsc_data;
+		size_t wsc_data_len;
+		uint8_t *wsc_ie;
+		size_t wsc_ie_len;
+
+		wsc_resp.response_type = WSC_RESPONSE_TYPE_AP;
+		wsc_resp.version2 = sta->wsc_v2;
+
+		wsc_data = wsc_build_association_response(&wsc_resp,
+								&wsc_data_len);
+		if (!wsc_data) {
+			l_error("wsc_build_beacon error");
+			goto send_frame;
+		}
+
+		wsc_ie = ie_tlv_encapsulate_wsc_payload(wsc_data, wsc_data_len,
+							&wsc_ie_len);
+		l_free(wsc_data);
+
+		if (!wsc_ie) {
+			l_error("ie_tlv_encapsulate_wsc_payload error");
+			goto send_frame;
+		}
+
+		memcpy(resp->ies + ies_len, wsc_ie, wsc_ie_len);
+		ies_len += wsc_ie_len;
+		l_free(wsc_ie);
+	}
+
+send_frame:
 	return ap_send_mgmt_frame(ap, mpdu, resp->ies + ies_len - mpdu_buf,
 					true, callback, sta);
 }
@@ -1030,8 +1064,8 @@ static int ap_parse_supported_rates(struct ie_tlv_iter *iter,
  */
 static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
 				const struct mmpdu_field_capability *capability,
-				uint16_t listen_interval,
-				struct ie_tlv_iter *ies)
+				uint16_t listen_interval, const uint8_t *ies,
+				size_t ies_len)
 {
 	struct ap_state *ap = sta->ap;
 	const char *ssid = NULL;
@@ -1040,6 +1074,9 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
 	struct l_uintset *rates = NULL;
 	struct ie_rsn_info rsn_info;
 	int err;
+	struct ie_tlv_iter iter;
+	uint8_t *wsc_data = NULL;
+	ssize_t wsc_data_len;
 
 	if (sta->assoc_resp_cmd_id)
 		return;
@@ -1049,16 +1086,20 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
 		goto unsupported;
 	}
 
-	while (ie_tlv_iter_next(ies))
-		switch (ie_tlv_iter_get_tag(ies)) {
+	wsc_data = ie_tlv_extract_wsc_payload(ies, ies_len, &wsc_data_len);
+
+	ie_tlv_iter_init(&iter, ies, ies_len);
+
+	while (ie_tlv_iter_next(&iter))
+		switch (ie_tlv_iter_get_tag(&iter)) {
 		case IE_TYPE_SSID:
-			ssid = (const char *) ie_tlv_iter_get_data(ies);
-			ssid_len = ie_tlv_iter_get_length(ies);
+			ssid = (const char *) ie_tlv_iter_get_data(&iter);
+			ssid_len = ie_tlv_iter_get_length(&iter);
 			break;
 
 		case IE_TYPE_SUPPORTED_RATES:
 		case IE_TYPE_EXTENDED_SUPPORTED_RATES:
-			if (ap_parse_supported_rates(ies, &rates) < 0) {
+			if (ap_parse_supported_rates(&iter, &rates) < 0) {
 				err = MMPDU_REASON_CODE_INVALID_IE;
 				goto bad_frame;
 			}
@@ -1066,16 +1107,26 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
 			break;
 
 		case IE_TYPE_RSN:
-			if (ie_parse_rsne(ies, &rsn_info) < 0) {
+			/*
+			 * WSC v2.0.5 Section 8.2:
+			 * "Note that during the WSC association [...] the
+			 * RSN IE and the WPA IE are irrelevant and shall be
+			 * ignored by both the station and AP."
+			 */
+			if (wsc_data)
+				break;
+
+			if (ie_parse_rsne(&iter, &rsn_info) < 0) {
 				err = MMPDU_REASON_CODE_INVALID_IE;
 				goto bad_frame;
 			}
 
-			rsn = (const uint8_t *) ie_tlv_iter_get_data(ies) - 2;
+			rsn = (const uint8_t *) ie_tlv_iter_get_data(&iter) - 2;
 			break;
 		}
 
-	if (!rates || !ssid || !rsn || ssid_len != strlen(ap->config->ssid) ||
+	if (!rates || !ssid || (!wsc_data && !rsn) ||
+			ssid_len != strlen(ap->config->ssid) ||
 			memcmp(ssid, ap->config->ssid, ssid_len)) {
 		err = MMPDU_REASON_CODE_INVALID_IE;
 		goto bad_frame;
@@ -1086,19 +1137,82 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
 		goto unsupported;
 	}
 
-	if (rsn_info.mfpr && rsn_info.spp_a_msdu_required) {
-		err = MMPDU_REASON_CODE_UNSPECIFIED;
-		goto unsupported;
-	}
+	/* Is the client requesting RSNA establishment or WSC registration */
+	if (!rsn) {
+		struct wsc_association_request wsc_req;
+		struct ap_event_registration_start_data event_data;
+		struct ap_wsc_pbc_probe_record *record;
 
-	if (!(rsn_info.pairwise_ciphers & ap->ciphers)) {
-		err = MMPDU_REASON_CODE_INVALID_PAIRWISE_CIPHER;
-		goto unsupported;
-	}
+		if (wsc_parse_association_request(wsc_data, wsc_data_len,
+							&wsc_req) < 0) {
+			err = MMPDU_REASON_CODE_INVALID_IE;
+			goto bad_frame;
+		}
 
-	if (rsn_info.akm_suites != IE_RSN_AKM_SUITE_PSK) {
-		err = MMPDU_REASON_CODE_INVALID_AKMP;
-		goto unsupported;
+		l_free(wsc_data);
+		wsc_data = NULL;
+
+		if (wsc_req.request_type !=
+				WSC_REQUEST_TYPE_ENROLLEE_OPEN_8021X) {
+			err = MMPDU_REASON_CODE_INVALID_IE;
+			goto bad_frame;
+		}
+
+		if (!ap->wsc_pbc_timeout) {
+			l_debug("WSC association from %s but we're not in "
+				"PBC mode", util_address_to_string(sta->addr));
+			err = MMPDU_REASON_CODE_UNSPECIFIED;
+			goto bad_frame;
+		}
+
+		if (l_queue_isempty(ap->wsc_pbc_probes)) {
+			l_debug("%s tried to register as enrollee but we "
+				"don't have their Probe Request record",
+				util_address_to_string(sta->addr));
+			err = MMPDU_REASON_CODE_UNSPECIFIED;
+			goto bad_frame;
+		}
+
+		/*
+		 * For PBC, the Enrollee must have sent the only PBC Probe
+		 * Request within the monitor time and walk time.
+		 */
+		record = l_queue_peek_head(ap->wsc_pbc_probes);
+		if (memcmp(sta->addr, record->mac, 6)) {
+			l_debug("Session overlap during %s's attempt to "
+				"register as WSC enrollee",
+				util_address_to_string(sta->addr));
+			err = MMPDU_REASON_CODE_UNSPECIFIED;
+			goto bad_frame;
+		}
+
+		memcpy(sta->wsc_uuid_e, record->uuid_e, 16);
+		sta->wsc_v2 = wsc_req.version2;
+
+		event_data.mac = sta->addr;
+		ap->event_func(AP_EVENT_REGISTRATION_START, &event_data,
+				ap->user_data);
+
+		/*
+		 * Since we're starting the PBC Registration Protocol
+		 * we can now exit the "active PBC mode".
+		 */
+		ap_wsc_exit_pbc(ap);
+	} else {
+		if (rsn_info.mfpr && rsn_info.spp_a_msdu_required) {
+			err = MMPDU_REASON_CODE_UNSPECIFIED;
+			goto unsupported;
+		}
+
+		if (!(rsn_info.pairwise_ciphers & ap->ciphers)) {
+			err = MMPDU_REASON_CODE_INVALID_PAIRWISE_CIPHER;
+			goto unsupported;
+		}
+
+		if (rsn_info.akm_suites != IE_RSN_AKM_SUITE_PSK) {
+			err = MMPDU_REASON_CODE_INVALID_AKMP;
+			goto unsupported;
+		}
 	}
 
 	/* 802.11-2016 11.3.5.3 j) */
@@ -1128,10 +1242,12 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
 	if (sta->assoc_rsne)
 		l_free(sta->assoc_rsne);
 
-	sta->assoc_rsne = l_memdup(rsn, rsn[1] + 2);
+	if (rsn)
+		sta->assoc_rsne = l_memdup(rsn, rsn[1] + 2);
+	else
+		sta->assoc_rsne = NULL;
 
-	sta->assoc_resp_cmd_id = ap_assoc_resp(ap, sta, sta->addr, sta->aid, 0,
-						reassoc,
+	sta->assoc_resp_cmd_id = ap_assoc_resp(ap, sta, sta->addr, 0, reassoc,
 						ap_success_assoc_resp_cb);
 	if (!sta->assoc_resp_cmd_id)
 		l_error("Sending success (Re)Association Response failed");
@@ -1160,7 +1276,9 @@ bad_frame:
 	if (rates)
 		l_uintset_free(rates);
 
-	if (!ap_assoc_resp(ap, NULL, sta->addr, 0, err, reassoc,
+	l_free(wsc_data);
+
+	if (!ap_assoc_resp(ap, sta, sta->addr, err, reassoc,
 				ap_fail_assoc_resp_cb))
 		l_error("Sending error (Re)Association Response failed");
 }
@@ -1174,7 +1292,6 @@ static void ap_assoc_req_cb(const struct mmpdu_header *hdr, const void *body,
 	const uint8_t *from = hdr->address_2;
 	const struct mmpdu_association_request *req = body;
 	const uint8_t *bssid = netdev_get_address(ap->netdev);
-	struct ie_tlv_iter iter;
 
 	l_info("AP Association Request from %s", util_address_to_string(from));
 
@@ -1184,7 +1301,7 @@ static void ap_assoc_req_cb(const struct mmpdu_header *hdr, const void *body,
 
 	sta = l_queue_find(ap->sta_states, ap_sta_match_addr, from);
 	if (!sta) {
-		if (!ap_assoc_resp(ap, NULL, from, 0,
+		if (!ap_assoc_resp(ap, NULL, from,
 				MMPDU_REASON_CODE_STA_REQ_ASSOC_WITHOUT_AUTH,
 				false, ap_fail_assoc_resp_cb))
 			l_error("Sending error Association Response failed");
@@ -1192,9 +1309,9 @@ static void ap_assoc_req_cb(const struct mmpdu_header *hdr, const void *body,
 		return;
 	}
 
-	ie_tlv_iter_init(&iter, req->ies, body_len - sizeof(*req));
 	ap_assoc_reassoc(sta, false, &req->capability,
-				L_LE16_TO_CPU(req->listen_interval), &iter);
+				L_LE16_TO_CPU(req->listen_interval),
+				req->ies, body_len - sizeof(*req));
 }
 
 /* 802.11-2016 9.3.3.8 */
@@ -1206,7 +1323,6 @@ static void ap_reassoc_req_cb(const struct mmpdu_header *hdr, const void *body,
 	const uint8_t *from = hdr->address_2;
 	const struct mmpdu_reassociation_request *req = body;
 	const uint8_t *bssid = netdev_get_address(ap->netdev);
-	struct ie_tlv_iter iter;
 	int err;
 
 	l_info("AP Reassociation Request from %s",
@@ -1227,13 +1343,13 @@ static void ap_reassoc_req_cb(const struct mmpdu_header *hdr, const void *body,
 		goto bad_frame;
 	}
 
-	ie_tlv_iter_init(&iter, req->ies, body_len - sizeof(*req));
 	ap_assoc_reassoc(sta, true, &req->capability,
-				L_LE16_TO_CPU(req->listen_interval), &iter);
+				L_LE16_TO_CPU(req->listen_interval),
+				req->ies, body_len - sizeof(*req));
 	return;
 
 bad_frame:
-	if (!ap_assoc_resp(ap, NULL, from, 0, err, true, ap_fail_assoc_resp_cb))
+	if (!ap_assoc_resp(ap, NULL, from, err, true, ap_fail_assoc_resp_cb))
 		l_error("Sending error Reassociation Response failed");
 }
 
-- 
2.25.1

  parent reply	other threads:[~2020-08-28 12:46 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-08-28 12:46 [PATCH 1/8] wscutil: Use a utility for building authorized_macs Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 2/8] wscutil: Add wsc_build_beacon Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 3/8] eapol: Handle the use_eapol_start flag on authenticator Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 4/8] ap: Stop ongoing handshake on reassociation Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 5/8] ap: Push Button mode API and beacon changes Andrew Zaborowski
2020-08-28 12:46 ` [PATCH 6/8] ap: WSC Probe Request processing logic Andrew Zaborowski
2020-08-28 12:46 ` Andrew Zaborowski [this message]
2020-08-28 12:46 ` [PATCH 8/8] ap: Start EAP-WSC authentication with WSC enrollees Andrew Zaborowski
2020-08-28 15:55 ` [PATCH 1/8] wscutil: Use a utility for building authorized_macs Denis Kenzior

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200828124649.78677-7-andrew.zaborowski@intel.com \
    --to=andrew.zaborowski@intel.com \
    --cc=iwd@lists.01.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox